1.邻接表的简介:
图的邻接矩阵存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。邻接表由表头结点和表结点两部分组成,其中图中每个顶点均对应一个存储在数组中的表头结点。如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。如词条概念图所示,表结点存放的是邻接顶点在数组中的索引。对于无向图来说,使用邻接表进行存储也会出现数据冗余,表头结点A所指链表中存在一个指向C的表结点的同时,表头结点C所指链表也会存在一个指向A的表结点。[1]
邻接表是图的一种最主要存储结构,用来描述图上的每一个点。对图的每个顶点建立一个容器(n个顶点建立n个容器),第i个容器中的结点包含顶点Vi的所有邻接顶点。实际上我们常用的邻接矩阵就是一种未离散化每个点的边集的邻接表。
在有向图中,描述每个点向别的节点连的边(点a->点b这种情况)。
在无向图中,描述每个点所有的边(点a-点b这种情况)
与邻接表相对应的存图方式叫做边集表,这种方法用一个容器存储所有的边。
工业上有很多非常好的图库的实现,例如C++的boost graph库.如果可以,尽量用这些库,这样可以大大提高你的效率。
2.表示法
注意:
n个顶点e条边的无向图的邻接表表示中有n个顶点表结点和2e个边表结点。(换句话说,每条边(i,j)在邻接表 中出现两次:一次在关于i的邻接表中,另一次在关于j的邻接表中)
有向图
对于有向图,vi的邻接表中每个表结点都对应于以vi为始点射出的一条边。因此,将有向图的邻接表称为出边表。
【例】有向图G6如下图所示,其中顶点v1的邻接表上两个表结点中的顶点序号分别为0和4,它们分别表示从v1射出的两条边(简称为v1的出边):<v1,v0>和<v1,v4>。
注意:
n个顶点e条边的有向图,它的邻接表表示中有n个顶点表结点和e个边表结点。(因为有向图是单向的)
逆邻接表
在有向图中,为图中每个顶点vi建立一个入边表的方法称逆邻接表表示法。
入边表中的每个表结点均对应一条以vi为终点(即射入vi)的边。
【例】G6的逆邻表如上面(b)图所示,其中v0的入边表上两个表结点1和3分别表示射人v0的两条边(简称为v0的入边):<v1,v0>和<v3,v0>。
注意:
n个顶点e条边的有向图,它的逆邻接表表示中有n个顶点表结点和e个边表结点。
3.邻接表的形式说明。
邻接表是一个二维容器,第一维描述某个点,第二维描述这个点所对应的边集们。
实现邻接表的方法绝对有100种以上。即使是前向星这种东西也是邻接表,因为它还是描述某个点和这个点所对应的边集们.
我们说说常用的邻接表存图法(静态的array就不说了.)必须有开O1以及以上编译的条件,不然没有测试的效率无任何意义。
第一维是描述点的。可以用vector,list,forward_list,deque,map,multimap,unordered_map,unordered_multimap等(一般不能用set,mutiset,unordered_set,unordered_multiset).
按照你的要求去选择。一般来讲存完图以后不涉及点的加入与删除优先使用vector.map,multimap,unordered_map,unordered_multimap.
第二维是描述这个点的边集,可以用全部的容器。也是的一般来讲存完图以后,不涉及点的加入与删除优先使用vector,空间充足可以考虑deque.涉及点的删除用forward_list或者是list,map,multimap,unordered_map,unordered_multimap.
#include <iostream> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <queue> using namespace std; #define N 200 typedef enum GraphKind {DG,DN,UDG,UDN}; typedef char VertexType[20]; typedef struct ArcNode { int adjvex;//该弧指向的顶点的位置 int *info; //该弧的相关信息 ArcNode *nextarc;//指向下一条弧的指针 }; typedef struct { VertexType data;//顶点信息 ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 } VNode,AdjList[N]; typedef struct ALGraph { AdjList vertices; int vexnum,arcnum;//弧的当前顶点数和弧数 int kind;//图的种类 }; //1.顶点的位置: int LocateVex(ALGraph g,VertexType u) { int i; for(i=0; i<g.vexnum; i++) { if(strcmp(g.vertices[i].data,u)==0) return i; } return -1; } //2.采用邻接表存储结构,构造没有相关信息的图G //(用一个函数构造4种图) void CreateGraph(ALGraph &G) { int i, j, k; int w; //权值 VertexType va , vb ; ArcNode *p; cout << "请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3):" << endl; cin >> G.kind; cout << "请输入图的顶点数,边数: " << endl; cin >> G.vexnum >> G.arcnum; cout << "请输入" << G.vexnum << "个顶点的值(" << "1" << "个字符)每个用空格隔开:" << endl; for( i = 0; i <G.vexnum; i++ ) //构造顶点向量 { cin >> G.vertices[i].data; G.vertices[i].firstarc = NULL; } if( G.kind == 1 || G.kind == 3 ) //网 cout << "请顺序输入每条弧(边)的权值,弧尾和弧头(以空格作为间隔):" << endl; else //图 cout << "请顺序输入每条弧边的弧尾和弧头(以空格作为间隔):" << endl; for( k = 0; k < G.arcnum; k++ ) //构造表结点链表 { if( G.kind == 1 || G.kind == 3 ) //网 cin >> w >> va >> vb; else //图 cin >> va >> vb; i = LocateVex( G, va ); //弧头 j = LocateVex( G, vb ); //弧尾 p = ( ArcNode * )malloc( sizeof( ArcNode ) ); p->adjvex = j; if( G.kind == 1 || G.kind == 3 ) { p->info = ( int * )malloc( sizeof( int ) ); *( p->info ) = w; } else p->info = NULL; p->nextarc = G.vertices[i].firstarc; //插在表头 G.vertices[i].firstarc = p; if( G.kind >= 2 ) //无向图或网,产生第二个表结点 { p = ( ArcNode* )malloc( sizeof( ArcNode ) ); p->adjvex = i; if( G.kind == 3 ) //无向图 { p->info = ( int * )malloc( sizeof( int ) ); *( p->info ) = w; } else p->info = NULL; //无向图 p->nextarc = G.vertices[j].firstarc; //插在表头 G.vertices[j].firstarc = p; } } } void Prt(ALGraph G) { int i; printf("vexnum :%d, arcnum :%d.\n\n",G.vexnum,G.arcnum); printf("VexList:\n"); for(i = 0; i < G.vexnum; i++){ printf(" v(%d):%s|",i,G.vertices[i].data); ArcNode *p = G.vertices[i].firstarc; while(p){ printf("->%d",p->adjvex); p = p->nextarc; } printf("\n"); } } void find(ALGraph &g,VertexType a,VertexType b) { int i,j; i=LocateVex(g,a); j=LocateVex(g,b); ArcNode *p,*q1,*q2; if(g.kind>=2) { p=g.vertices[i].firstarc; int flag=0; while(p) { if(p->adjvex==j) { flag=1; break; } p=p->nextarc; } if(flag==1) { cout<<"YES"<<endl; if(g.kind%2==1) cout<<*(p->info)<<endl; } else { cout<<"NO"<<endl; } } else { q1=g.vertices[i].firstarc; int flag1=0,flag2=0; while(q1) { if(q1->adjvex==j) { flag1=1; break; } q1=q1->nextarc; } q2=g.vertices[j].firstarc; while(q2) { if(q2->adjvex==i) { flag2=1; break; } q2=q2->nextarc; } if(flag1==1) { cout<<a<<"->"<<b<<endl; if(g.kind%2==1) cout<<*(q1->info)<<endl; } else if(flag2==1) { cout<<b<<"->"<<a<<endl; if(g.kind%2==1) cout<<*(q2->info)<<endl; } else { cout<<"NO"<<endl; } } } void degree(ALGraph &g,VertexType a) { int i,j,k; ArcNode *p,*q; if(g.kind>1) { int sum=0; i=LocateVex(g,a); p=g.vertices[i].firstarc; while(p) { sum++; p=p->nextarc; } cout<<a<<"的度为:"<<sum<<endl; } else { int sum1=0,sum2=0; i=LocateVex(g,a); p=g.vertices[i].firstarc; while(p) { sum1++; p=p->nextarc; } for(j=0; j<g.vexnum; j++) { if(j!=i) { q=g.vertices[j].firstarc; while(q) { if(q->adjvex==i) { sum2++; } q=q->nextarc; } } } cout<<a<<"的度为:"<<(sum1+sum2)<<endl; } } bool vis[N]; //返回v的第一个邻接顶点的序号,若图中无邻接顶点,返回-1 int FirstAdjVex(ALGraph G,VertexType v){ int i = LocateVex(G,v); ArcNode *p = G.vertices[i].firstarc; if(p){ return p->adjvex; } else{ return -1; } } //返回v的(相对于w的)下一个邻接顶点的序号。若w是v的最后一个邻接顶点,则返回-1 int NextAdjVex(ALGraph G,VertexType v,VertexType w){ int i = LocateVex(G,v); int j = LocateVex(G,w); ArcNode *p = G.vertices[i].firstarc; while(p){ if(p->adjvex == j) break; p = p->nextarc; } if(!p || !p->nextarc){ //没找到w或w是最后一个邻接点 return -1; } else{ return (p->nextarc)->adjvex; } } //深度优先搜索 void DFS(ALGraph G,int v){ int w; vis[v] = true; printf("%s ",G.vertices[v].data); //访问第v个顶点 w = FirstAdjVex(G,G.vertices[v].data); while(w >= 0){ if(!vis[w]){ DFS(G,w); } w = NextAdjVex(G,G.vertices[v].data,G.vertices[w].data); } } //对图做深度优先遍历 void DFSTraverse(ALGraph G){ int v; memset(vis,false,sizeof(vis)); for(v = 0; v < G.vexnum; v++){ if(!vis[v]){ DFS(G,v);//对尚未访问的顶点调用DFS } } printf("\n"); } //对图做广度优先搜索 void BFSTraverse(ALGraph G) { int v,u,w; queue<int>q; memset(vis,false,sizeof(vis)); for(v = 0; v < G.vexnum; v++){ //如果是连通图,v = 0就遍历全图 if(!vis[v]){ vis[v] = true; printf("%s ",G.vertices[v].data); } q.push(v); while(!q.empty()){ u=q.front(); q.pop(); //队头元素出队,并置为u w = FirstAdjVex(G,G.vertices[u].data); while(w >= 0){ if(!vis[w]){ vis[w] = true; printf("%s ",G.vertices[w].data); q.push(w);//入队 } w = NextAdjVex(G,G.vertices[u].data,G.vertices[w].data); } } } printf("\n"); } int main() { ALGraph g; CreateGraph(g); printf("输出邻接表:\n"); Prt(g); VertexType a,b; printf("输入两个顶点:\n"); cin>>a>>b; printf("输出顶点之间是否存在边:\n"); find(g,a,b); printf("输入一个顶点:\n"); cin>>a; printf("输出该点的度:\n"); degree(g,a); printf("深度优先搜索遍历:\n"); DFSTraverse(g); printf("广度优先搜索遍历:\n"); BFSTraverse(g); return 0; }