圖的鄰接矩陣表示:
下面的這個程序讀入一組定義一個無向圖的便,創建一個對應這個圖的鄰接矩陣。如果在圖中頂點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