图的邻接矩阵和邻接链表表示
2012-07-28 18:37 youxin 阅读(8015) 评论(0) 编辑 收藏 举报图的邻接矩阵表示:
下面的这个程序读入一组定义一个无向图的便,创建一个对应这个图的邻接矩阵。如果在图中顶点i,j或j,i之间有一条边,就把a[i][j]和a[j][i]置为1,如果不存在这样的边,则置0。
#include<iostream> using namespace std; int main() { int N; int i,j; cin>>N; int **adj=new int*[N]; for( i=0;i<N;i++) adj[i]=new int[N]; for(i=0;i<N;i++) for(j=0;j<N;j++) adj[i][j]=0; for(i=0;i<N;i++) adj[i][i]=1; while(cin>>i>>j) { adj[i][j]=1; adj[j][i]=1; } }
另一种图的直观表示方法是链表数组,也叫邻接表(adjacent list),我们为每个顶点保存一个链表。
这2中方法都是简单数据结构的数组-----都对每个顶点描述了和该顶点关联的边。对邻接矩阵,这个简单数据结构实现为一个索引数组;对邻接列表,则实现为一个链表。
#include<iostream> #include<cstdlib> using namespace std; #define N 8 struct node { int v; node *next; node(int x,node *t):v(x),next(t){ } }; typedef node *Link; int main() { freopen("input.txt","r",stdin); int i,j; Link adj[N]; for(i=0;i<N;i++) adj[i]=0; while(cin>>i>>j) { adj[j]=new node(i,adj[j]); adj[i]=new node(j,adj[i]); } for(i=0;i<N;i++) { cout<<i<<"\t"; while(adj[i]->next!=NULL) { cout<<adj[i]->v<<"--->"; adj[i]=adj[i]->next; } cout<<adj[i]->v<<endl; } }
输出的语句是我自己加的。(algorithms in c++ parts1-4本没有)。看下面图:
从链表中我们要注意,前面的0,1,2,3,4只是数组中的序号,并不是节点。对于无向图,如果在i的链表中存在节点j,则在j的链表中必定存在节点i。数组的第i个位置,包含了一个链表指针,链表中为每个和i链接的顶点保存一个节点。
程序运行过程如下:
输入 0 1
adj[1]=new node(0,NULL); //为数组的第1个位置建立0节点
adj[0]=new node(1,NULL); 为数组的第0个位置建立1节点
输入 0 2
adj[2]=new node(0,adj[2]) //为数组的第2个位置建立0节点
adj[0]=new node(2,adj[0]); //这里很重要,adj[0]不是NULL了,代表了1节点,新建立的2节点后面的next为1节点,说明我们建立链表的顺序是从右向左的。
所以我们输出时,2应该在前,1在后。
我们输入:
0 1
0 2
0 5
0 6
0 7
5 4
4 6
5 3
4 3
4 7
1 7
2 7
输出:
可以看到,在数组的第0个位置,2在1前面。注意;邻接链表的输出与输入有关系。所以输出不是唯一的。
我们现在顶点从1开始而不是从0开始:
#include<iostream> #include<cstdlib> using namespace std; #define N 8 typedef struct Node { int data; Node *next; Node(int x,Node *t) { data=x; next=t; } }*LinkNode; typedef struct{ LinkNode *adj; int vexnum,arcnum; int kind;//图的种类标志 }Graph; void DFS(Graph G,int v); static bool visited[100]; void (*visitFunc)(int v); void visit(int v) { cout<<v<<ends; } void DFSTraverse(Graph G,void (*Visit)(int v)) { visitFunc=Visit; for(int v=1;v<=G.vexnum;v++) visited[v]=false; for(int v=1;v<=G.vexnum;v++) { if(!visited[v]) DFS(G,v); } } void DFS(Graph G,int v) { visited[v]=true; visitFunc(v); LinkNode m,n; m=G.adj[v]; while(m!=NULL ) { if(!visited[m->data]) DFS(G,m->data); m=m->next; } } int main() { freopen("有向图邻接表.txt","r",stdin); //从1开始 Graph graph; int vexnum,arcnum; cin>>vexnum>>arcnum; graph.vexnum=vexnum; graph.arcnum=arcnum; LinkNode *adj=new LinkNode[graph.vexnum+1]; for(int i=1;i<=vexnum;i++) adj[i]=0; int start,end;//起点,终点 for(int i=1;i<=arcnum;i++) { cin>>start>>end; adj[start]=new Node(end,adj[start]); adj[end]=new Node(start,adj[end]); } LinkNode *adj2=new LinkNode[graph.vexnum+1]; for(int i=1;i<=vexnum;i++) { adj2[i]=adj[i]; } for(int i=1;i<=vexnum;i++) { cout<<i<<"\t"; while(adj[i]->next!=NULL) { cout<<adj[i]->data<<"--->"; adj[i]=adj[i]->next; } cout<<adj[i]->data<<endl; } //必须重置adj[i];因为现在adj[i]已经指向的最后一个节点,为了重置,之前必须保存adj[i] for(int i=1;i<=vexnum;i++) { adj[i]=adj2[i]; } for(int i=1;i<=vexnum;i++) { cout<<i<<"\t"; cout<<adj[i]->data<<endl; } graph.adj=adj; DFSTraverse(graph,visit); }
按严蔚敏书上P168的图,输入:
8
9
1 2
1 3
2 4
2 5
3 6
3 7
4 8
5 8
6 7
输出:
我们按照严蔚敏书上关于邻接表的严格定义,写出如下代码:
#include<iostream> using namespace std; typedef int InfoType; typedef struct ArcNode{ int adjvex;//该狐所指向的顶点位置 ArcNode *nextarc;//指向下一条弧的指针 InfoType *info; }ArcNode; typedef char VextexType[10] ; typedef struct VNode{ VextexType data; //顶点信息 ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 }VNode,Adj[100]; typedef struct { Adj vertices; int vexnum,arcnum; int kind; }Graph; int locateVex(Graph &G,VextexType u) { for(int i=0;i<G.vexnum;i++) if(strcmp(u,G.vertices[i].data)==0)//如果VexTexType 为char []类型 //if(u==G.vertices[i].data)//如果VextexType为数值型,用这句 return i; return -1; } void createGraph(Graph &G) { cout<<"请输入图的类型:有向图0,有向网1,无向图2,无向网3:"<<endl; cin>>G.kind; cout<<"输入顶点数和边数"<<endl; cin>>G.vexnum>>G.arcnum; cout<<"请输入"<<G.vexnum<<"个顶点的值\n"; for(int i=0;i<G.vexnum;i++)//构造顶点向量 { cin>>G.vertices[i].data; G.vertices[i].firstarc=NULL; } if(G.kind==1||G.kind==3)//网 cout<<"请输入每条弧(边)的权值、弧尾和弧头(以空格作为间隔):\n"; else //图 cout<<"请输入每条弧(边)的弧尾和弧头(以空格作为间隔):\n"; VextexType va,vb; int w; for(int k=0;k<G.arcnum;k++) { if(G.kind==1||G.kind==3) cin>>w>>va>>vb; else cin>>va>>vb; int i=locateVex(G,va);//弧尾 int j=locateVex(G,vb);//弧头 ArcNode *p=new ArcNode(); p->adjvex=j; if(G.kind==1||G.kind==3) { p->info=new int(w); } else p->info=NULL; p->nextarc=G.vertices[i].firstarc;//插在表头 G.vertices[i].firstarc=p; if(G.kind>=2) //无向的 ,产生第二个表节点 { p=new ArcNode(); p->adjvex=i; if(G.kind==3) //无向网 { p->info=new int(w); } else p->info=NULL; p->nextarc=G.vertices[j].firstarc; G.vertices[j].firstarc=p; } }//end for } int main() { freopen("邻接表2.txt","r",stdin); Graph G; createGraph(G); for(int i=0;i<G.vexnum;i++) { cout<<G.vertices[i].data<<"\t"; ArcNode *p=G.vertices[i].firstarc; while(p!=NULL) { cout<<G.vertices[p->adjvex].data<<"--->"; p=p->nextarc; } cout<<endl; } }
Adj vertices[i]存储的第i个顶点的信息,是一个结构体,里面的data该顶点的数据,firstarc表示指向的第一条狐。
该程序同前面的程序实际上都是一样,都是在之前插入。
2 //无向图
8
9
v1 v2 v3 v4 v5 v6 v7 v8
v1 v2
v1 v3
v2 v4
v2 v5
v3 v6
v3 v7
v4 v8
v5 v8
v6 v7
可以看到更 前面的一样。
只不过这种程序灵活性更大。
DFS遍历代码:
bool visited[100]; void (*visitFunc)(VextexType v); void visit(VextexType v) { cout<<v<<ends; } void DFS(Graph G,int v);//函数声明 void DFSTraverse(Graph G,void (*Visit)(VextexType v)) { visitFunc=Visit; for(int v=0;v<G.vexnum;v++) visited[v]=false; for(int v=0;v<G.vexnum;v++) { if(!visited[v]) DFS(G,v); } } void DFS(Graph G,int v) { visited[v]=true; visitFunc(G.vertices[v].data); ArcNode *p=G.vertices[v].firstarc; while(p!=0) { int w=p->adjvex; if(!visited[w]) DFS(G,w); p=p->nextarc; } }
遍历上面的图输出:
v1 v3 v7 v6 v2 v5 v8 v4。
有几点值得注意,
visitFunc(G.vertices[v].data);不是visitFunc(v).
因为我们不是遍历顶点的序号,而是得到顶点的信息。
测试有向网:
1
4 5
a b c d
3 a b
2 a c
2 b c
4 b d
5 d c
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通