算法导论——基本的图算法
對于圖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)的時間完成,效率較高。對于無向圖必定是一個對稱矩陣,可以用上三角或下三角來壓縮存儲,而有向圖通過行->列的形式來表示方向,可以在矩陣中存儲邊的權重。
廣度優先搜索
?廣度優先搜索是指,從已發現結點和為發現結點之間的邊間沿著廣度方向向外擴展,對一個結點k來說,首先探索與他直接相鄰的所有結點,然后再去發現其他間接相鄰的節點。
如圖的無向單位圖白色代表未知的結點,灰色是已知但未探索完周邊相鄰結點的結點,黑色是已經發現完直接相鄰結點的已知結點。要求出從s到所有結點的最短路徑長度。(a)開始只有s是已知的將s存入隊列。(b)從隊列中取出s檢查直接相鄰的結點有w和r,他們的最短路徑長是1,存入隊列然后s被標記為黑色。(c)從隊列中取出w,檢查w直接相鄰的未知結點有t和x,路徑長度為2,同樣入隊并將w涂黑。依次出隊檢測完所有結點之后,可以得到所有點的最短路徑長度即(i)所示。代碼方面BFS算法通常是借助隊列來存儲已發現等待進行周邊探索的結點。
#include<stdio.h> #include<queue> using namespace std; #define SIZE 10int 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 10int 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);//若該結點為被訪問過則對他進行dfsvisit[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 10int 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);//若該結點為被訪問過則對他進行dfsvisit[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,他們之間可以相互到達。這樣的集合即為強聯通分量。
如圖所示的陰影部分各自是一個強聯通分量。可以通過對圖G的每個節點進行DFS獲得他能夠到達的所有結點,然后對圖G進行轉置再進行一次每個結點能夠到達結點的計算。當且僅當兩個結點可以相互到達時他們屬于同一個強連通分量。?
#include<stdio.h> using namespace std; #define SIZE 10int 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);//若該結點未被訪問過則對他進行dfsvisit[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; }?
個人GitHub地址: https://github.com/GrayWind33總結
以上是生活随笔為你收集整理的算法导论——基本的图算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WPF,强制捕获鼠标事件,鼠标移出控件外
- 下一篇: Docker发布应用程序指南