1. 圖的構造部分
采用鄰接矩陣存儲邊。節點編號為數字,從0~n-1,n為節點個數
class Graphs { public: Graphs(int n){ m_VeticeNum = n; m_Edge.resize(n); m_Edge[0].resize(n); }
void InitEdge(vector<vector<int>> edge) { m_Edge = edge; } private: int m_VeticeNum; //節點個數,為了方便,設定節點編號為0~m_VeticeNum-1 vector<vector<int>> m_Edge; };
初始化時,需要指定n,並傳入鄰接矩陣。
2. 圖的遍歷
對於圖的遍歷部分,原理參考圖的深度優先遍歷和廣度優先遍歷。下面是代碼實現:
int Graphs::findNeighbor(int v, int idx) //找到節點v,從idx之后的第一個鄰接點 { for (int i = idx + 1; i < m_VeticeNum; i++) { if (m_Edge[v][i] < INT_MAX && m_Edge[v][i] >0) return i; } return -1; } //找到從idx以后的v的下一個鄰接點
DFS:
void Graphs::DFS(int v) { visited[v] = true; int w = findNeighbor(v, 0);//找到第一個鄰接點 while (w != -1) { if (!visited[w]) { cout << "訪問節點:" << w << endl; visited[w] = true; DFS(w); } w = findNeighbor(v, w); } }
BFS:
void Graphs::BFS(int v) { queue<int> q; q.push(v); visited[v] = true; cout << "訪問節點:" << v << endl; while (!q.empty()) { int node = q.front(); q.pop(); //找鄰接點 int w = findNeighbor(node, 0); while (w != -1) { if (!visited[w]) { q.push(w); cout << "訪問節點:" << w << endl; visited[w] = true; } w = findNeighbor(node, w); } } }
3. Dijkstra算法
Dijkstra算法原理參考最短路徑-Dijkstra和Floyd。其中的算法步驟個人認為下面的更好理解:
有兩個集合,一個是已經更新的有最短路徑的集合S,一個是待選擇的最短路徑集合U,初始時數組distance[]初始化為無窮,算法流程如下:
(1) 初始節點v,將v加入S,更新distance[v]=0,然后在所有U中尋找節點u1,u1與v相鄰且distance[v]+edge[v][u1]最小,當distance[v]+edge[v][u1]<distance[v]時,更新distance[u1];
(2) 將u添加到S,然后以新的節點u為跳板,在U中尋找新的節點u2,更新distance[u2]+edge[u1][u2]<distance[u2],且distance[u2]最小的節點u3
(3) 重復步驟2。
下面看具體代碼:
1 vector<int> Graphs::Dijkstra(int v0) //返回最短距離 2 { 3 vector<bool> S(m_VeticeNum, 0); //記錄已經求出最短路徑的節點 4 vector<int> dis(m_VeticeNum, INT_MAX); //記錄最短距離 5 S[v0] = true; 6 int u = v0; 7 dis[v0] = 0; 8 for (int i = 1; i < m_VeticeNum; i++) //每次添加一個節點到S,共需要m_VeticeNum-1次 9 { 10 //找出新的最短路徑點加入S 11 int minidx=0; 12 int minDis = INT_MAX; 13 for (int j = 0; j < m_VeticeNum; j++) 14 { 15 if (!S[j]) 16 { 17 if(m_Edge[u][j]>0 && m_Edge[u][j] <INT_MAX && dis[u] + m_Edge[u][j] < dis[j]) 18 dis[j] = dis[u] + m_Edge[u][j]; //更新距離 19 if (minDis > dis[j] && S[j]==false) 20 { 21 minDis = dis[j]; 22 minidx = j; 23 } 24 } 25 } 26 u = minidx; 27 S[u] = true; 28 } 29 return dis; 30 }
求出來的是從節點V0到所有其他節點的最短路徑。
如果要求出最短距離的路徑,再添加一個數組pre[],用於記錄前一個節點,只需要在18行更新距離后,記錄前一個節點,即pre[j]=u;
4. Floyd算法
Floyd算法原理參考Floyd。但是該博文沒有講路徑求解,路徑記錄參考博文Floyd算法求多元最短路徑。
下面是代碼:
vector<vector<int>> Graphs::Floyd(void) { vector<vector<int>> e(m_VeticeNum, vector<int>(m_VeticeNum,0)); /* 如果需要記錄路徑,path[i][j]表示從i到j時到達j的前一步路徑 vector<vector<int>> path(m_VeticeNum, vector<int>(m_VeticeNum, 0)); for (int i = 0; i < m_VeticeNum; i++) { for (int j = 0; j < m_VeticeNum; j++) path[i][j] = i; } */ e = m_Edge; //初始的路徑 for (int k = 0; k < m_VeticeNum; k++) //對於每一個中間節點k { for (int i = 0; i < m_VeticeNum; i++) //從節點i到節點j,中間經過k { for (int j = 0; j < m_VeticeNum; j++) { if (e[i][k] > 0 && e[i][k] < INT_MAX && e[k][j]>0 && e[k][j]<INT_MAX && e[i][j]>e[i][k] + e[k][j]) { e[i][j] = e[i][k] + e[k][j]; //path[i][j]=path[k][j]; } } } } return e; }
該函數求出來的是任意節點到i到節點j的最短路徑。
5. 相關例題
下面我們看一個最短路徑的例子:leetcode 743 網絡延遲時間。從題意可以分析出,該題目要求節點K到其他所有節點距離的最大值。采用Djikstra算法即能求出節點K到所有其他節點的最小距離。此處給出的輸入不是鄰接矩陣,而是所有邊,可以直接在所有邊上遍歷,並實時更新dis數組,同時每次添加一個新的節點到已訪問過的節點中,代碼如下:
int networkDelayTime(vector<vector<int>>& times, int N, int K) { vector<int> visited(N,false); //visited[i]表示節點i-1是否已訪問 vector<int> dis(N,INT_MAX); //表示到各個節點的距離 dis[K-1]=0; visited[K-1]=true; //初始化dis for(int i=0;i<size(times);i++) { if(times[i][0]==K) dis[times[i][1]-1]=times[i][2]; } int u=K; for(int i=0;i<N-1;i++) //N個節點,每次添加一個到visited,需要N-1次 { int mindis=INT_MAX; int idx=0; for(int j=0;j<size(times);j++) { if(times[j][0]==u && visited[times[j][1]-1]==false && dis[u-1]+times[j][2]<dis[times[j][1]-1]) dis[times[j][1]-1]=dis[u-1]+times[j][2]; //更新dis數組 if(mindis>dis[times[j][1]-1] && visited[times[j][1] - 1] == false) { mindis=dis[times[j][1]-1]; //尋找一個未訪問的節點添加到visited idx=times[j][1]; } } if(mindis==INT_MAX) //沒有距離可以更新,說明有不可達的節點 break; u=idx; visited[u-1]=true; //將新的最近節點添加到visited } int maxdis=0; for(int i=0;i<N;i++) if(dis[i]>maxdis) maxdis=dis[i]; return maxdis==INT_MAX? -1:maxdis; }