對於圖G=(V,E),V表明點,E表明邊。圖有兩種標準的表示方法:鄰接矩陣法和鄰接鏈表法。算法
鄰接鏈表法適合表示邊的條數少的稀疏圖,能夠節約存儲空間。對於有向圖G來講,邊(u,v)必定會出如今鏈表Adj[u]中,所以,全部鏈表的長度之和必定等於|E|。對於無向圖來講,邊(u,v)會同時出如今Adj[u]和Adj[v]中,所以全部鏈表的長度之和必定等於2|E|。可是鄰接鏈表要獲取某條邊(u,v)的信息必須遍歷Adj[u] ,當邊數較多時,由於鏈表過長會增大計算時間。經過在鏈表結點增長屬性能夠附帶信息,好比邊的權重。函數
鄰接矩陣法會存在必定的空間冗餘,但全部邊的信息獲取均可以在O(1)的時間完成,效率較高。對於無向圖一定是一個對稱矩陣,能夠用上三角或下三角來壓縮存儲,而有向圖經過行->列的形式來表示方向,能夠在矩陣中存儲邊的權重。spa
廣度優先搜索是指,從已發現結點和爲發現結點之間的邊間沿着廣度方向向外擴展,對一個結點k來講,首先探索與他直接相鄰的全部結點,而後再去發現其餘間接相鄰的節點。code
如圖的無向單位圖白色表明未知的結點,灰色是已知但未探索完周邊相鄰結點的結點,黑色是已經發現完直接相鄰結點的已知結點。要求出從s到全部結點的最短路徑長度。(a)開始只有s是已知的將s存入隊列。(b)從隊列中取出s檢查直接相鄰的結點有w和r,他們的最短路徑長是1,存入隊列而後s被標記爲黑色。(c)從隊列中取出w,檢查w直接相鄰的未知結點有t和x,路徑長度爲2,一樣入隊並將w塗黑。依次出隊檢測完全部結點以後,能夠獲得全部點的最短路徑長度即(i)所示。代碼方面BFS算法一般是藉助隊列來存儲已發現等待進行周邊探索的結點。blog
#include<stdio.h> #include<queue> using namespace std; #define SIZE 10 int G[SIZE][SIZE];//鄰接矩陣,參數初始化略 int length[SIZE];//最短路徑長度 int known[SIZE]; void bfs (int start){ queue<int> queue; queue.push(start); length[start] = 0; while(queue.size() != 0){ int temp = queue.front();//因爲離開始結點近的點必定會先入隊,因此算法會按照距離開始結點的順序進行遍歷,即廣度優先 queue.pop(); for(int i = 0; i < SIZE; i++){ if(known[i] != 1 && G[i][temp] == 1){ //存在路徑且該點未被發現過,標記該點爲已知,最短路徑長更新爲檢查結點+1,加入隊列 known[i] = 1; length[i] = 1 + length[temp]; queue.push(i); } } } }
上面提到廣度優先搜索是先探索完該結點周邊一圈以後,再從這一圈中的某個點開始探索它周邊的一圈。深度優先搜索的策略則是,在圖中儘量的深刻,順着一條路徑探索直到該結點全部的相鄰邊都是已被探索過的,而後回到該路徑上一個前驅結點繼續該過程。因爲後探索到的結點會再到達盡頭後馬上開始出發探索,因此能夠利用棧結構後進先出的特色完成DFS算法,也可使用遞歸。排序
#include<stdio.h> #include<stack> using namespace std; #define SIZE 10 int G[SIZE][SIZE];//鄰接矩陣,參數初始化略 int length[SIZE];//最短路徑長度,初始化length[start]=0,其餘爲正無窮 int visit[SIZE];//該結點是否已被訪問過 void dfs(int start){ for(int i = 0; i < SIZE; i++){ if(G[start][i] == 1){//選擇該結點相鄰的路徑 if(length[start] + 1 < length[i]) length[i] = length[start] + 1;//檢查最小路徑是不是最短的 if(visit[i] == 0){ dfs(i);//若該結點爲被訪問過則對他進行dfs visit[i] = 1; } } } } void dfsQueue(int start){ length[start] = 0; stack<int> stack; stack.push(start); while(stack.size() != 0){ int temp = stack.top();//獲取棧頂元素 stack.pop(); for(int i = 0; i < SIZE; i++){ if(G[temp][i] != 0){ length[i] = (length[temp] + 1) < length[i] ? length[temp] + 1 : length[i]; if(visit[i] == 0){ queue.push(i); visit[i] = 1;//避免同一個點被重複入棧 } } } } }
對於一個無環圖來講,若是存在邊(u,v)則u的拓撲排序在v的前面。實際例子來講,咱們必需要先穿襪子再穿鞋子,先穿內衣再穿外套,這就是拓撲排序。遞歸
如圖所示,將(a)中的拓撲順序排成(b)中的實際的操做順序。拓撲排序能夠經過DFS來實現,從第一個點到最後一個點,若以前沒有被探索過且沒有前驅點就調用DFS,全部點被探索的前後次序就是最後的排序。隊列
#include<stdio.h> #include<vector> using namespace std; #define SIZE 10 int G[SIZE][SIZE];//鄰接矩陣,參數初始化略 int length[SIZE];//最短路徑長度,初始化length[start]=0,其餘爲正無窮 int visit[SIZE];//該結點是否已被訪問過 vector<int> path;//最後的排序結果 void dfs(int start){ path.push_back(start); for(int i = 0; i < SIZE; i++){ if(G[start][i] == 1){//選擇該結點相鄰的路徑 if(length[start] + 1 < length[i]) length[i] = length[start] + 1;//檢查最小路徑是不是最短的 if(visit[i] == 0){ dfs(i);//若該結點爲被訪問過則對他進行dfs visit[i] = 1; } } } } int main(void){ int i,j; for(i = 0; i < SIZE; i++){ if(visit[i] == 1) continue; for(j = 0; j < SIZE; j++){ if(G[j][i] == 1){ break; } } if(j == SIZE){ dfs(i);//只有未被探索過,沒有先驅路徑的點會在主函數被調用 } } return 0; }
強連通份量是指在有向圖中,存在一個最大的結點集合C,對於C中的任意一對結點u和v來講,同時存在路徑u→v和v→u,他們之間能夠相互到達。這樣的集合即爲強聯通份量。it
如圖所示的陰影部分各自是一個強聯通份量。能夠經過對圖G的每一個節點進行DFS得到他可以到達的全部結點,而後對圖G進行轉置再進行一次每一個結點可以到達結點的計算。當且僅當兩個結點能夠相互到達時他們屬於同一個強連通份量。 io
#include<stdio.h> using namespace std; #define SIZE 10 int G[SIZE][SIZE];//鄰接矩陣,參數初始化略 int visit[SIZE];//該結點是否已被訪問過 int num;//統計連通量個數 int part[2][SIZE];//兩次連通量記錄 int res[SIZE];//最終結果 void dfs(int start, int time){; part[time][start] = num;// for(int i = 0; i < SIZE; i++){ if(G[start][i] == 1){//選擇該結點相鄰的路徑 if(visit[i] == 0){ dfs(i, time);//若該結點未被訪問過則對他進行dfs visit[i] = 1; } } } } void init(){ int i; for(i = 0; i < SIZE; i++) visit[i] = 0; num = 0; } int main(void){ int i,j, temp; init(); for(i = 0; i < SIZE; i++){ if(visit[i] == 0){ dfs(i, 0); num++; } } //圖的轉置 for(i = 0; i < SIZE; i++){ for(j = i + 1; j < SIZE; j++){ temp = G[i][j]; G[i][j] = G[j][i]; G[j][i] = temp; } } init(); for(i = SIZE - 1; i >= 0; i--){ if(visit[i] == 0){ dfs(i, 1); num++; } } init(); for(i = 0; i < SIZE; i++){ if(visit[i] == 1) continue;//已經確認屬於某個連通份量的結點跳過下面的查探步驟 res[i] = num++; for(j = 0; j < SIZE; j++){ if(i != j && part[0][i] == part[0][j] && part[1][i] == part[1][j]){ res[j] = res[i];//若i和j在兩圖中都屬於同個連通量,則他們屬於同一個強連通量 visit[j] = 1; } } visit[i] = 1; } return 0; }