目錄
二、圖的深度遍歷(dfs)和廣度遍歷(bfs)---鄰接矩陣存儲
一、圖的存儲
圖的存儲結構主要分兩種,一種是鄰接矩陣,一種是鄰接表。
1.1 鄰接矩陣
圖的鄰接矩陣存儲方式是用兩個數組來表示圖。一個一維數組存儲圖中頂點信息,一個二維數組(鄰接矩陣)存儲圖中的邊或弧的信息。
設圖G有n個頂點,則鄰接矩陣是一個n*n的方陣,定義爲:
看一個實例,下圖左就是一個無向圖。
從上面可以看出,無向圖的邊數組是一個對稱矩陣。所謂對稱矩陣就是n階矩陣的元滿足aij = aji。即從矩陣的左上角到右下角的主對角線爲軸,右上角的元和左下角相對應的元全都是相等的。
從上面可以看出,無向圖的邊數組是一個對稱矩陣。所謂對稱矩陣就是n階矩陣的元滿足aij = aji。即從矩陣的左上角到右下角的主對角線爲軸,右上角的元和左下角相對應的元全都是相等的。
從這個矩陣中,很容易知道圖中的信息。
(1)要判斷任意兩頂點是否有邊無邊就很容易了;
(2)要知道某個頂點的度,其實就是這個頂點vi在鄰接矩陣中第i行或(第i列)的元素之和;
(3)求頂點vi的所有鄰接點就是將矩陣中第i行元素掃描一遍,arc[i][j]爲1就是鄰接點;
而有向圖講究入度和出度,頂點vi的入度爲1,正好是第i列各數之和。頂點vi的出度爲2,即第i行的各數之和。
若圖G是網圖,有n個頂點,則鄰接矩陣是一個n*n的方陣,定義爲:
1.2 鄰接表
鄰接矩陣是不錯的一種圖存儲結構,但是,對於邊數相對頂點較少的圖,這種結構存在對存儲空間的極大浪費。因此,找到一種數組與鏈表相結合的存儲方法稱爲鄰接表。
鄰接表的處理方法是這樣的:
(1)圖中頂點用一個一維數組存儲,當然,頂點也可以用單鏈表來存儲,不過,數組可以較容易的讀取頂點的信息,更加方便。
(2)圖中每個頂點vi的所有鄰接點構成一個線性表,由於鄰接點的個數不定,所以,用單鏈表存儲,無向圖稱爲頂點vi的邊表,有向圖則稱爲頂點vi作爲弧尾的出邊表。
例如,下圖就是一個無向圖的鄰接表的結構。
從圖中可以看出,頂點表的各個結點由data和firstedge兩個域表示,data是數據域,存儲頂點的信息,firstedge是指針域,指向邊表的第一個結點,即此頂點的第一個鄰接點。邊表結點由adjvex和next兩個域組成。adjvex是鄰接點域,存儲某頂點的鄰接點在頂點表中的下標,next則存儲指向邊表中下一個結點的指針。
對於帶權值的網圖,可以在邊表結點定義中再增加一個weight的數據域,存儲權值信息即可。如下圖所示。
3.兩者區別
對於一個具有n個頂點e條邊的無向圖
它的鄰接表表示有n個頂點表結點2e個邊表結點
對於一個具有n個頂點e條邊的有向圖
它的鄰接表表示有n個頂點表結點e個邊表結點
如果圖中邊的數目遠遠小於n2稱作稀疏圖,這是用鄰接表表示比用鄰接矩陣表示節省空間;
如果圖中邊的數目接近於n2,對於無向圖接近於n*(n-1)稱作稠密圖,考慮到鄰接表中要附加鏈域,採用鄰接矩陣表示法爲宜。
二、圖的深度遍歷(dfs)和廣度遍歷(bfs)---鄰接矩陣存儲
輸入矩陣: 0 1 0 0 0 0 0 1 0 0 |
2.1 圖的深度遍歷(dfs)
1、從頂點v出發深度遍歷圖G的算法:① 訪問v, ② 依次從頂點v未被訪問的鄰接點出發深度遍歷。
2、一點心得:dfs算法最大特色就在於其遞歸特性,使得算法代碼簡潔。
2.2 圖的廣度遍歷(bfs)
1、從頂點v出發遍歷圖G的算法:
①訪問v,②假設最近一層的訪問頂點依次爲vi1,vi2,vi3...vik,則依次訪問vi1,vi2,vi3...vik的未被訪問的鄰接點
③重複②知道沒有未被訪問的鄰接點爲止
2、一點心得:bfs算法其實就是一種層次遍歷算法。從算法描述可以看到該算法要用到隊列這一數據結構。這裏用STL中的<queue>實現。
#include<iostream> using namespace std; int a[11][11]; bool visited[11]; void store_graph() //鄰接矩陣存儲圖 { int i,j; for(i=1;i<=10;i++) for(j=1;j<=10;j++) cin>>a[i][j]; } void dfs_graph() //深度遍歷圖 { void dfs(int v);//dfs在該方法下面,所以需要定義 memset(visited,false,sizeof(visited)); for(int i=1;i<=10;i++) //遍歷每個頂點是爲了防止圖不連通時無法訪問每個頂點 if(visited[i]==false) dfs(i); } void dfs(int v) //深度遍歷頂點 { int Adj(int x);//Adj在該方法下面,所以需要定義 cout<<v<<" "; //訪問頂點v visited[v]=true; int adj=Adj(v); while(adj!=0) { if(visited[adj]==false) dfs(adj); //遞歸調用是實現深度遍歷的關鍵所在 adj=Adj(v); } } int Adj(int x) //求鄰接點 { for(int i=1;i<=10;i++) if(a[x][i]==1 && visited[i]==false) return i; return 0; } int main() { cout<<"初始化圖:"<<endl; store_graph(); cout<<"dfs遍歷結果:"<<endl; dfs_graph(); return 0; } |
#include<iostream> #include<queue> using namespace std; int a[11][11]; bool visited[11]; void store_graph() { for(int i=1;i<=10;i++) for(int j=1;j<=10;j++) cin>>a[i][j]; } void bfs_graph() { void bfs(int v); memset(visited,false,sizeof(visited)); for(int i=1;i<=10;i++) if(visited[i]==false) bfs(i); } void bfs(int v) { int Adj(int x); queue<int> myqueue; int adj,temp; cout<<v<<" "; visited[v]=true; myqueue.push(v); while(!myqueue.empty()) //隊列非空表示還有頂點未遍歷到 { temp=myqueue.front(); //獲得隊列頭元素 myqueue.pop(); //頭元素出對 adj=Adj(temp); while(adj!=0) { if(visited[adj]==false) { cout<<adj<<" "; visited[adj]=true; myqueue.push(adj); //進對 } adj=Adj(temp); } } } int Adj(int x) { for(int i=1;i<=10;i++) if(a[x][i]==1 && visited[i]==false) return i; return 0; } int main() { cout<<"初始化圖:"<<endl; store_graph(); cout<<"bfs遍歷結果:"<<endl; bfs_graph(); return 0; } |