图,不是可以三言两语说得通的数据结构,比如树严格来说也是一种图,如果树中带环,就变成了图。
正文 如果不知道本节的重点,请先阅读标题。接下来我们直接开始。
首先,图你可以通俗的理解为几座城市,比如ABCD四座,A和B之间有城际公路,B和C有城际公路,D没有往外的城际公路,那么只要是个酱紫就知道A可以去B去C,B可以去A去C,C可以去A去B,D无处可去。在图中,我们把“城际高速”称为边,城市称作点或结点,而像D这样的城市叫“孤点”或“孤岛”。很通俗易懂,对吧?
然后是单向图与无向图,仍然通过举例来说明。
假设A点和B点连通,
但只能从A点去B点,这就是单向连通;
如果A点可以去B点,B点也可以去A点,这就是双向连通。
那么你可能会说,这样讲图不就是个数学模型嘛?编程上不是什么用也没有吗?那么我们现在引入两个概念,及出度and入度。你可以这么想,一个结点可以直接去到的结点的个数(不可以绕路)叫做出度,而可以直接去到该结点的结点个数(不可以绕路)叫做入度。
还是很简单的道理,有了以上概念,还是要进入代码环节。如何存储图呢?(提前预告一下,关于图的其他知识,比如图的搜索、洪水填充、完全图等知识会在下一节提到)
关于存储图的方法,可谓是群英集萃,目前作者已知的有4种,邻接矩阵、邻接点列表以及结构体数组和链式前向星。下文会分开赘述。
邻接矩阵
顾名思义,邻接矩阵就是把图的结点的连通关系保存在一个矩阵中。
优点很明显,就是可以很清楚地知道A点与B点是否连通。
但缺点也很多,比如空间的限制,假设我现在有10000个结点,a[10001][10001] 这么大的数组是开不出来的。
接下来演示邻接矩阵的存储方法。
#define MAXN 1008
bool a[MAXN][MAXN];
int n,m,nodex,nodey;
cin>>n>>m; // 有n个点,m个邻接关系
for(int i=1;i<=n;i++){
cin>>nodex>>nodey;
a[nodex][nodey]=1;
// a[nodey][nodex]=1; 这一句只有双向连通时使用
}
我相信这应该是图最简单的存储方式了。
邻接点列表作者本人也是十分喜欢这种存储方式的。
优点是可以罗列出每一个点的所有直接连通点。
缺点也很明显,空间仍然可以hack该方法。
vector> aplist(1001); int a,b; cin>>a>>b; aplist[a].push_back(b); aplist[b].push_back(a); // 如果是单向连通这句就不要了
因为一个点可能有许多直接连接点,所以vector还是很不错的选择。
结构体数组应该是最LJ的图存储方式了,通常用struct或pair实现,这里演示struct。
优点:好像莫得
缺点:不可以直接明了的表示结点与结点的连通关系,甚至不如静态数组?
const int MAXN=1008;
struct node{
int x,y;
}a[MAXN];
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
}
链式前向星
听名字就知道贼高端,但是需要先掌握链表的知识,接下来仅展示一个模板
#includeusing namespace std; struct node{ int v; int w; int next; //下一条边的编号 }; int head[10001];// 头,出 node edge[1000000]; // 第i条边 int cnt; void add(int x,int y,int z){ edge[cnt].v=y; edge[cnt].w=z; edge[cnt].next=head[x]; head[x]=cnt++; return ; } int main (){ memset(head,-1,sizeof(head)); int n,m; cin>>n>>m; int i; for(i=1;i<=m;i++){ int u,v,w; add(u,v,w); add(v,u,w);//无向图才要写 } //遍历节点1的所有出边 for(i=head[1];~i;i=edge[i].next){ // 呱啦呱啦 } return 0; }
其实还是比较简单的啦。
以上就是图的所有存储方式啦!如果你觉得这篇blog还不错的话可以点个免费的赞嘛QwQ?



