數據結構之圖
圖(Graph)
包含
一組頂點:通常用V (Vertex) 表示頂點集合
一組邊:通常用E (Edge) 表示邊的集合
邊是頂點對:(v, w) ∈E ,其中v, w ∈ V
有向邊<v, w> 表示從v指向w的邊(單行線)
不考慮重邊和自回路
無向圖:邊是無向邊(v, w)
有向圖:邊是有向邊<v, w>
連通:如果從V到W存在一條(無向)路徑,則稱V和W是連通的
連通圖(Connected Graph):如果對於圖的任一兩個頂點v、w∈V,v和w都是連通的,則稱該圖為連通圖。圖中任意兩頂點均連通。
連通分量(Connected Component):無向圖中的極大連通子圖。
極大頂點數:再加1個頂點就不連通了
極大邊數:包含子圖中所有頂點相連的所有邊
強連通:有向圖中頂點V和W之間存在雙向路徑,則稱V和W是強連通的。
強連通圖:有向圖中任意兩頂點均強連通。
強連通分量:有向圖的極大強連通子圖。
路徑:V到W的路徑是一系列頂點{V, v1, v2, …,vn, W}的集合,其中任一對相鄰的頂點間都有圖中的邊。路徑的長度是路徑中的邊數(如果帶權,則是所有邊的權重和)。
如果V到W之間的所有頂點都不同,則稱簡單路徑
回路:起點等於終點的路徑
一.鄰接矩陣
圖的鄰接矩陣存儲方式就是用一個二維數組來表示。
鄰接矩陣G[N][N]——N個頂點從0到N-1編號
頂點i、j有邊,則G[i][j] = 1 或邊的權重
鄰接矩陣的優點
直觀、簡單、好理解
方便檢查任意一對頂點間是否存在邊
方便找任一頂點的所有“鄰接點”(有邊直接相連的頂點)
方便計算任一頂點的“度”(從該點發出的邊數為“出度”,指向該點的邊數為“入度”)
無向圖:對應行(或列)非0元素的個數
有向圖:對應行非0元素的個數是“出度”;對應列非0元素的個數是“入度”
鄰接矩陣的缺點
浪費空間—— 存稀疏圖(點很多而邊很少)有大量無效元素
對稠密圖(特別是完全圖)還是很合算的
浪費時間—— 統計稀疏圖中一共有多少條邊

1 /* ͼµÄÁÚ½Ó¾ØÕó±íʾ·¨ */ 2 #include <iostream> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <queue> 6 using namespace std; 7 8 #define MaxVertexNum 100 /* ×î´ó¶¥µãÊýÉèΪ100 */ 9 #define INFINITY 65535 /* ÉèΪ˫×Ö½ÚÎÞ·ûºÅÕýÊýµÄ×î´óÖµ65535*/ 10 typedef int Vertex; /* Óö¥µãϱê±íʾ¶¥µã,ΪÕûÐÍ */ 11 typedef int WeightType; /* ±ßµÄȨֵÉèΪÕûÐÍ */ 12 typedef char DataType; /* ¶¥µã´æ´¢µÄÊý¾ÝÀàÐÍÉèΪ×Ö·ûÐÍ */ 13 14 /* ±ßµÄ¶¨Òå */ 15 typedef struct ENode *PtrToENode; 16 struct ENode{ 17 Vertex V1, V2; /* ÓÐÏò±ß<V1, V2> */ 18 WeightType Weight; /* È¨ÖØ */ 19 }; 20 typedef PtrToENode Edge; 21 22 /* ͼ½áµãµÄ¶¨Òå */ 23 typedef struct GNode *PtrToGNode; 24 struct GNode{ 25 int Nv; /* ¶¥µãÊý */ 26 int Ne; /* ±ßÊý */ 27 WeightType G[MaxVertexNum][MaxVertexNum]; /* ÁÚ½Ó¾ØÕó */ 28 DataType Data[MaxVertexNum]; /* ´æ¶¥µãµÄÊý¾Ý */ 29 /* ×¢Ò⣺ºÜ¶àÇé¿öÏ£¬¶¥µãÎÞÊý¾Ý£¬´ËʱData[]¿ÉÒÔ²»ÓóöÏÖ */ 30 }; 31 typedef PtrToGNode MGraph; /* ÒÔÁÚ½Ó¾ØÕó´æ´¢µÄͼÀàÐÍ */ 32 bool Visited[MaxVertexNum] = {false}; 33 34 MGraph CreateGraph( int VertexNum ); 35 void InsertEdge( MGraph Graph, Edge E ); 36 MGraph BuildGraph(); 37 bool IsEdge( MGraph Graph, Vertex V, Vertex W ); 38 void InitVisited(); 39 Vertex BFS ( MGraph Graph, Vertex S, void (*Visit)(Vertex) ); 40 Vertex DFS ( MGraph Graph, Vertex S, void (*Visit)(Vertex) ); 41 Vertex listDFS( MGraph Graph, void (*Visit)(Vertex) ); 42 void DFSListComponents( MGraph Graph, void (*Visit)(Vertex) ); 43 void BFSListComponents( MGraph Graph, void (*Visit)(Vertex) ); 44 45 MGraph CreateGraph( int VertexNum ) 46 { /* ³õʼ»¯Ò»¸öÓÐVertexNum¸ö¶¥µãµ«Ã»ÓбߵÄͼ */ 47 Vertex V, W; 48 MGraph Graph; 49 50 Graph = (MGraph)malloc(sizeof(struct GNode)); /* ½¨Á¢Í¼ */ 51 Graph->Nv = VertexNum; 52 Graph->Ne = 0; 53 /* ³õʼ»¯ÁÚ½Ó¾ØÕó */ 54 /* ×¢Ò⣺ÕâÀïĬÈ϶¥µã±àºÅ´Ó0¿ªÊ¼£¬µ½(Graph->Nv - 1) */ 55 for (V=0; V<Graph->Nv; V++) 56 for (W=0; W<Graph->Nv; W++) 57 Graph->G[V][W] = INFINITY; 58 59 return Graph; 60 } 61 62 void InsertEdge( MGraph Graph, Edge E ) 63 { 64 /* ²åÈë±ß <V1, V2> */ 65 Graph->G[E->V1][E->V2] = E->Weight; 66 /* ÈôÊÇÎÞÏòͼ£¬»¹Òª²åÈë±ß<V2, V1> */ 67 Graph->G[E->V2][E->V1] = E->Weight; 68 } 69 70 MGraph BuildGraph() 71 { 72 MGraph Graph; 73 Edge E; 74 Vertex V; 75 int Nv, i; 76 77 scanf("%d", &Nv); /* ¶ÁÈë¶¥µã¸öÊý */ 78 Graph = CreateGraph(Nv); /* ³õʼ»¯ÓÐNv¸ö¶¥µãµ«Ã»ÓбߵÄͼ */ 79 80 scanf("%d", &(Graph->Ne)); /* ¶ÁÈë±ßÊý */ 81 if ( Graph->Ne != 0 ) { /* Èç¹ûÓÐ±ß */ 82 E = (Edge)malloc(sizeof(struct ENode)); /* ½¨Á¢±ß½áµã */ 83 /* ¶ÁÈë±ß£¬¸ñʽΪ"Æðµã ÖÕµã È¨ÖØ"£¬²åÈëÁÚ½Ó¾ØÕó */ 84 for (i=0; i<Graph->Ne; i++) { 85 scanf("%d %d %d", &E->V1, &E->V2, &E->Weight); 86 /* ×¢Ò⣺Èç¹ûÈ¨ÖØ²»ÊÇÕûÐÍ£¬WeightµÄ¶ÁÈë¸ñʽҪ¸Ä */ 87 InsertEdge( Graph, E ); 88 } 89 } 90 91 /* Èç¹û¶¥µãÓÐÊý¾ÝµÄ»°£¬¶ÁÈëÊý¾Ý */ 92 for (V=0; V<Graph->Nv; V++) 93 scanf(" %c", &(Graph->Data[V])); 94 95 return Graph; 96 } 97 /* ÁÚ½Ó¾ØÕó´æ´¢µÄͼ - BFS */ 98 99 /* IsEdge(Graph, V, W)¼ì²é<V, W>ÊÇ·ñͼGraphÖеÄÒ»Ìõ±ß£¬¼´WÊÇ·ñVµÄÁڽӵ㡣 */ 100 /* ´Ëº¯Êý¸ù¾ÝͼµÄ²»Í¬ÀàÐÍÒª×ö²»Í¬µÄʵÏÖ£¬¹Ø¼üÈ¡¾öÓÚ¶Ô²»´æÔڵıߵıíʾ·½·¨¡£*/ 101 /* ÀýÈç¶ÔÓÐȨͼ, Èç¹û²»´æÔڵı߱»³õʼ»¯ÎªINFINITY, Ôòº¯ÊýʵÏÖÈçÏÂ: */ 102 bool IsEdge( MGraph Graph, Vertex V, Vertex W ) 103 { 104 return Graph->G[V][W]<INFINITY ? true : false; 105 } 106 107 //³õʼ»¯ Visited[] = false 108 void InitVisited() 109 { 110 for(int i = 0; i < MaxVertexNum; i++) 111 Visited[i] = false; 112 } 113 114 void Visit(Vertex v) 115 { 116 printf("%d ",v); 117 } 118 119 /* Visited[]Ϊȫ¾Ö±äÁ¿£¬ÒѾ³õʼ»¯Îªfalse */ 120 Vertex BFS ( MGraph Graph, Vertex S, void (*Visit)(Vertex) ) 121 { /* ÒÔSΪ³ö·¢µã¶ÔÁÚ½Ó¾ØÕó´æ´¢µÄͼGraph½øÐÐBFSËÑË÷ */ 122 queue<Vertex> Q; 123 Vertex V, W; 124 125 /* ·ÃÎʶ¥µãS£º´Ë´¦¿É¸ù¾Ý¾ßÌå·ÃÎÊÐèÒª¸Äд */ 126 Visit( S ); 127 Visited[S] = true; /* ±ê¼ÇSÒÑ·ÃÎÊ */ 128 Q.push(S); /* SÈë¶ÓÁÐ */ 129 130 while ( !Q.empty() ) { 131 V = Q.front(); 132 Q.pop(); /* µ¯³öV */ 133 for( W=0; W < Graph->Nv; W++ ) /* ¶ÔͼÖеÄÿ¸ö¶¥µãW */ 134 /* ÈôWÊÇVµÄÁڽӵ㲢ÇÒδ·ÃÎʹý */ 135 if ( !Visited[W] && IsEdge(Graph, V, W) ) { 136 /* ·ÃÎʶ¥µãW */ 137 Visit( W ); 138 Visited[W] = true; /* ±ê¼ÇWÒÑ·ÃÎÊ */ 139 Q.push(W); /* WÈë¶ÓÁÐ */ 140 } 141 } /* while½áÊø*/ 142 //ÒÑÓà BFSListComponents( MGraph Graph, void (*Visit)(Vertex) )½øÐиĽø 143 // printf("\n"); 144 // 145 // //±éÀú Visited[]ÁгöËùÓÐBFSµÄ¶¥µã ÈôÖ»ÐèÒ»¸ö¶¥µã¿ªÊ¼µÄBFS¿ÉºöÂÔ 146 // Vertex i; 147 // for(i = 0; i < Graph->Nv; i++) { 148 // if(Visited[i] == false)//ÕÒ³öδ±»·ÃÎʹýµÄ½áµã¼Ç¼iÖµ 149 // break; 150 // } 151 // if(i == Graph->Nv) 152 // return 0; 153 // else 154 // return BFS(Graph,i,Visit); 155 } 156 157 /* ÒÔSΪ³ö·¢µã¶ÔÁÚ½Ó¾ØÕó´æ´¢µÄͼGraph½øÐÐDFSËÑË÷ */ 158 Vertex DFS ( MGraph Graph, Vertex S, void (*Visit)(Vertex) ) 159 { 160 Visited[S] = true; 161 Visit(S); 162 for(Vertex w = 0; w < Graph->Nv; w++) { 163 if( IsEdge(Graph, S, w) && Visited[w]==false) { 164 DFS(Graph,w,Visit); 165 } 166 } 167 } 168 //ÁгöDFSµÄËùÓж¥µã ÒÑÓÃDFSListComponents( MGraph Graph, void (*Visit)(Vertex) )½øÐиĽø 169 Vertex listDFS( MGraph Graph, void (*Visit)(Vertex) ) 170 { 171 Vertex i; 172 for(i = 0; i < Graph->Nv; i++) { 173 if(Visited[i] == false)//ÕÒ³öδ±»·ÃÎʹýµÄ½áµã¼Ç¼iÖµ 174 break; 175 } 176 if(i == Graph->Nv) 177 return 0; 178 DFS(Graph, i, Visit); 179 printf("\n"); 180 181 return listDFS(Graph,Visit); 182 } 183 void DFSListComponents( MGraph Graph, void (*Visit)(Vertex) ) 184 { 185 for(Vertex i = 0; i < Graph->Nv; i++) { 186 if(Visited[i] == false) { 187 DFS(Graph, i, Visit); 188 printf("\n"); 189 } 190 } 191 } 192 void BFSListComponents( MGraph Graph, void (*Visit)(Vertex) ) 193 { 194 for(Vertex i = 0; i < Graph->Nv; i++) { 195 if(Visited[i] == false) { 196 BFS(Graph, i, Visit); 197 printf("\n"); 198 } 199 } 200 } 201 202 int main() 203 { 204 MGraph graph; 205 graph = BuildGraph(); 206 InitVisited(); 207 listDFS(graph,&Visit); 208 InitVisited(); 209 DFSListComponents(graph,&Visit); 210 InitVisited(); 211 // BFS(graph,0,&Visit); 212 BFSListComponents(graph,&Visit); 213 return 0; 214 }
二.鄰接表
G[N]為指針數組,對應矩陣每行一個鏈表,只存非0元素。
鄰接表的優點
方便找任一頂點的所有“鄰接點”
節約稀疏圖的空間
需要N個頭指針+ 2E個結點(每個結點至少2個域)
方便計算任一頂點的“度”?
對無向圖:是的
對有向圖:只能計算“出度”;需要構造“逆鄰接表”(存指向自己的邊)來方便計算“入度”
鄰接表的缺點
不方便檢查任意一對頂點間是否存在邊

1 /* 圖的鄰接表表示法 */ 2 //build用的 頭插法 尾插法遍歷 出來不同 但無影響 3 #include <iostream> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <queue> 7 using namespace std; 8 9 #define MaxVertexNum 100 /* 最大頂點數設為100 */ 10 typedef int Vertex; /* 用頂點下標表示頂點,為整型 */ 11 typedef int WeightType; /* 邊的權值設為整型 */ 12 typedef char DataType; /* 頂點存儲的數據類型設為字符型 */ 13 14 /* 邊的定義 */ 15 typedef struct ENode *PtrToENode; 16 struct ENode{ 17 Vertex V1, V2; /* 有向邊<V1, V2> */ 18 WeightType Weight; /* 權重 */ 19 }; 20 typedef PtrToENode Edge; 21 22 /* 鄰接點的定義 */ 23 typedef struct AdjVNode *PtrToAdjVNode; 24 struct AdjVNode{ 25 Vertex AdjV; /* 鄰接點下標 */ 26 WeightType Weight; /* 邊權重 */ 27 PtrToAdjVNode Next; /* 指向下一個鄰接點的指針 */ 28 }; 29 30 /* 頂點表頭結點的定義 */ 31 typedef struct Vnode{ 32 PtrToAdjVNode FirstEdge;/* 邊表頭指針 */ 33 DataType Data; /* 存頂點的數據 */ 34 /* 注意:很多情況下,頂點無數據,此時Data可以不用出現 */ 35 } AdjList[MaxVertexNum]; /* AdjList是鄰接表類型 */ 36 37 /* 圖結點的定義 */ 38 typedef struct GNode *PtrToGNode; 39 struct GNode{ 40 int Nv; /* 頂點數 */ 41 int Ne; /* 邊數 */ 42 AdjList G; /* 鄰接表 */ 43 }; 44 typedef PtrToGNode LGraph; /* 以鄰接表方式存儲的圖類型 */ 45 bool Visited[MaxVertexNum] = {false}; 46 47 LGraph CreateGraph( int VertexNum ); 48 void InsertEdge( LGraph Graph, Edge E ); 49 LGraph BuildGraph(); 50 void Visit( Vertex V ); 51 void InitVisited(); 52 void DFS( LGraph Graph, Vertex V, void (*Visit)(Vertex) ); 53 Vertex listDFS( LGraph Graph, void (*Visit)(Vertex) ); 54 int BFS( LGraph Graph, Vertex V, void (*Visit)(Vertex) ); 55 void DFSListComponents( LGraph Graph, void (*Visit)(Vertex) ); 56 void BFSListComponents( LGraph Graph, void (*Visit)(Vertex) ); 57 58 LGraph CreateGraph( int VertexNum ) 59 { /* 初始化一個有VertexNum個頂點但沒有邊的圖 */ 60 Vertex V; 61 LGraph Graph; 62 63 Graph = (LGraph)malloc( sizeof(struct GNode) ); /* 建立圖 */ 64 Graph->Nv = VertexNum; 65 Graph->Ne = 0; 66 /* 初始化鄰接表頭指針 */ 67 /* 注意:這里默認頂點編號從0開始,到(Graph->Nv - 1) */ 68 for (V=0; V<Graph->Nv; V++) 69 Graph->G[V].FirstEdge = NULL; 70 71 return Graph; 72 } 73 74 void InsertEdge( LGraph Graph, Edge E ) 75 { 76 PtrToAdjVNode NewNode; 77 78 /* 插入邊 <V1, V2> */ 79 /* 為V2建立新的鄰接點 */ 80 NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode)); 81 NewNode->AdjV = E->V2; 82 NewNode->Weight = E->Weight; 83 /* 將V2插入V1的表頭 */ 84 NewNode->Next = Graph->G[E->V1].FirstEdge; 85 Graph->G[E->V1].FirstEdge = NewNode; 86 87 /* 若是無向圖,還要插入邊 <V2, V1> */ 88 /* 為V1建立新的鄰接點 */ 89 NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode)); 90 NewNode->AdjV = E->V1; 91 NewNode->Weight = E->Weight; 92 /* 將V1插入V2的表頭 */ 93 NewNode->Next = Graph->G[E->V2].FirstEdge; 94 Graph->G[E->V2].FirstEdge = NewNode; 95 } 96 97 LGraph BuildGraph() 98 { 99 LGraph Graph; 100 Edge E; 101 Vertex V; 102 int Nv, i; 103 104 scanf("%d", &Nv); /* 讀入頂點個數 */ 105 Graph = CreateGraph(Nv); /* 初始化有Nv個頂點但沒有邊的圖 */ 106 107 scanf("%d", &(Graph->Ne)); /* 讀入邊數 */ 108 if ( Graph->Ne != 0 ) { /* 如果有邊 */ 109 E = (Edge)malloc( sizeof(struct ENode) ); /* 建立邊結點 */ 110 /* 讀入邊,格式為"起點 終點 權重",插入鄰接矩陣 */ 111 for (i=0; i<Graph->Ne; i++) { 112 scanf("%d %d %d", &E->V1, &E->V2, &E->Weight); 113 /* 注意:如果權重不是整型,Weight的讀入格式要改 */ 114 InsertEdge( Graph, E ); 115 } 116 } 117 118 /* 如果頂點有數據的話,讀入數據 */ 119 for (V=0; V<Graph->Nv; V++) 120 scanf(" %c", &(Graph->G[V].Data)); 121 122 return Graph; 123 } 124 125 void Visit( Vertex V ) 126 { 127 printf("%d ", V); 128 } 129 130 //初始化 Visited[] = false 131 void InitVisited() 132 { 133 for(int i = 0; i < MaxVertexNum; i++) 134 Visited[i] = false; 135 } 136 137 /* Visited[]為全局變量,已經初始化為false */ 138 void DFS( LGraph Graph, Vertex V, void (*Visit)(Vertex) ) 139 { /* 以V為出發點對鄰接表存儲的圖Graph進行DFS搜索 */ 140 PtrToAdjVNode W; 141 142 Visit( V ); /* 訪問第V個頂點 */ 143 Visited[V] = true; /* 標記V已訪問 */ 144 145 for( W=Graph->G[V].FirstEdge; W; W=W->Next ) /* 對V的每個鄰接點W->AdjV */ 146 if ( !Visited[W->AdjV] ) /* 若W->AdjV未被訪問 */ 147 DFS( Graph, W->AdjV, Visit ); /* 則遞歸訪問之 */ 148 } 149 //已用InitVisited();進行改進 150 Vertex listDFS( LGraph Graph, void (*Visit)(Vertex) ) 151 { 152 Vertex i; 153 for(i = 0; i < Graph->Nv; i++) { 154 if(Visited[i] == false)//找出未被訪問過的結點記錄i值 155 break; 156 } 157 if(i == Graph->Nv) 158 return 0; 159 DFS(Graph, i, Visit); 160 printf("\n"); 161 return listDFS(Graph,Visit); 162 } 163 //圖不連通時 列出各連通分量 164 void DFSListComponents( LGraph Graph, void (*Visit)(Vertex) ) 165 { 166 for(Vertex i = 0; i < Graph->Nv; i++) { 167 if(Visited[i] == false) { 168 DFS(Graph, i, Visit); 169 printf("\n"); 170 } 171 } 172 } 173 int BFS( LGraph Graph, Vertex V, void (*Visit)(Vertex) ) 174 { 175 queue<Vertex> Q; 176 Vertex W; 177 178 Visit( V ); /* 訪問第V個頂點 */ 179 Visited[V] = true; /* 標記V已訪問 */ 180 Q.push(V); 181 182 while( !Q.empty() ) { 183 W = Q.front(); 184 Q.pop(); 185 for(PtrToAdjVNode tempV = Graph->G[W].FirstEdge; tempV; tempV=tempV->Next ) /* 對W的每個鄰接點tempV->AdjV */ 186 if( !Visited[tempV->AdjV]) { 187 Visited[tempV->AdjV] = true; 188 Visit(tempV->AdjV); 189 Q.push(tempV->AdjV); 190 } 191 } 192 //已用 BFSListComponents進行改進 193 // printf("\n"); 194 // 195 // //遍歷 Visited[]列出所有BFS的頂點 若只需一個頂點開始的BFS可忽略 196 // Vertex i; 197 // for(i = 0; i < Graph->Nv; i++) { 198 // if(Visited[i] == false)//找出未被訪問過的結點記錄i值 199 // break; 200 // } 201 // if(i == Graph->Nv) 202 // return 0; 203 // else 204 // return BFS(Graph,i,Visit); 205 return 0; 206 } 207 //圖不連通時 列出各連通分量 208 void BFSListComponents( LGraph Graph, void (*Visit)(Vertex) ) 209 { 210 for(Vertex i = 0; i < Graph->Nv; i++) { 211 if(Visited[i] == false) { 212 BFS(Graph, i, Visit); 213 printf("\n"); 214 } 215 } 216 } 217 218 219 int main() 220 { 221 LGraph graph; 222 graph = BuildGraph(); 223 InitVisited(); 224 listDFS(graph,&Visit); 225 InitVisited(); 226 DFSListComponents(graph,&Visit); 227 InitVisited(); 228 // BFS(graph, 0, &Visit); 229 BFSListComponents(graph,&Visit); 230 return 0; 231 }
三.BFS廣度優先搜索(Breadth First Search, BFS)
運用隊列,將頂點V的每個鄰接點進隊。(類似於樹的層先遍歷)
若有N個頂點、E條邊,時間復雜度是
用鄰接表存儲圖,有O(N+E)
用鄰接矩陣存儲圖,有O(N^2)
四.DFS深度優先搜索索(Depth First Search, DFS)
用遞歸(類似於樹的先序遍歷)。
ListComponents 圖不連通時,列出各連通分量。
若有N個頂點、E條邊,時間復雜度是
用鄰接表存儲圖,有O(N+E)
用鄰接矩陣存儲圖,有O(N^2)
五.最短路徑
兩個不同頂點之間的所有路徑中,邊的權值之和最小的那一條路徑
第一個頂點為源點(Source)
最后一個頂點為終點(Destination)
單源最短路徑問題:從某固定源點出發,求其到所有其他頂點的最短路徑
無權圖(無論是否有向):按照路徑長度遞增(非遞減)的順序找出到各個頂點的最短路
類似於BFS,運用隊列
dist[W] = S到W最短距離
dist[S] = 0;
path[W] = S到W路上經過的頂點
時間復雜度T = O(V + E)

1 /* dist[]和path[]全部初始化為-1 */ 2 void Unweighted ( LGraph Graph, int dist[], int path[], Vertex S ) 3 { 4 queue<Vertex> Q; 5 Vertex V; 6 PtrToAdjVNode W; 7 8 dist[S] = 0; /* 初始化源點 */ 9 Q.push(S); 10 11 while( !Q.empty() ){ 12 V = Q.front(); 13 Q.pop(); 14 for ( W = Graph->G[V].FirstEdge; W; W = W->Next ) /* 對V的每個鄰接點W->AdjV */ 15 if ( dist[W->AdjV] == -1 ) { /* 若W->AdjV未被訪問過 */ 16 dist[W->AdjV] = dist[V] + 1; /* W->AdjV到S的距離更新 */ 17 path[W->AdjV] = V; /* 將V記錄在S到W->AdjV的路徑上 */ 18 Q.push(W->AdjV); 19 } 20 } /* while結束*/ 21 }
有權圖(無論是否有向):按照遞增的順序找出到各個頂點的最短路
Dijkstra 算法
令S={源點s + 已經確定了最短路徑的頂點vi}
對任一未收錄的頂點v,定義dist[v]為s到v的最短路徑長度,但該路徑僅經過S中的頂點。即路徑{s-->(vi∈S)-->v}的最小長度
路徑是按照遞增(非遞減)的順序生成的,則
真正的最短路必須只經過S中的頂點(!!!) 因為是遞增的順序生成 如果頂點w不再路徑集合上 然而是最短,應該早就收錄了(意會。。。)
每次從未收錄的頂點中選一個dist最小的收錄(貪心)
增加一個v進入S,可能影響另外一個w的dist值!(如果收錄v使得s到w的路徑變短,則s到w的路徑一定經過v,並且v到w有一條邊)
dist[w] = min{dist[w], dist[v] + <v,w>的權重}
白話算法:
每次找到dist最小的值,即第一次找到S和第二個頂點dist最小的那個, 比較更新該頂點未訪問過的鄰接點的dist 然后一直找dist最小的
不能有負值圈
圖只更新dist[]中的值 不改變鄰接矩陣的值!

1 /* 鄰接矩陣存儲 - 有權圖的單源最短路算法 */ 2 Vertex FindMinDist( MGraph Graph, int dist[], int collected[] ) 3 { /* 返回未被收錄頂點中dist最小者 */ 4 Vertex MinV, V; 5 int MinDist = INFINITY; 6 7 for (V=0; V<Graph->Nv; V++) { 8 if ( collected[V]==false && dist[V] < MinDist) { 9 /* 若V未被收錄,且dist[V]更小 */ 10 MinDist = dist[V]; /* 更新最小距離 */ 11 MinV = V; /* 更新對應頂點 */ 12 } 13 } 14 if (MinDist < INFINITY) /* 若找到最小dist */ 15 return MinV; /* 返回對應的頂點下標 */ 16 else return ERROR; /* 若這樣的頂點不存在,返回錯誤標記 */ 17 } 18 19 bool Dijkstra( MGraph Graph, int dist[], int path[], Vertex S ) 20 { 21 int collected[MaxVertexNum]; 22 Vertex V, W; 23 24 /* 初始化:此處默認鄰接矩陣中不存在的邊用INFINITY表示 */ 25 for ( V=0; V < Graph->Nv; V++ ) { 26 dist[V] = Graph->G[S][V]; 27 path[V] = -1; 28 collected[V] = false; 29 } 30 /* 先將起點收入集合 */ 31 dist[S] = 0; 32 collected[S] = true; 33 34 while (1) { 35 /* V = 未被收錄頂點中dist最小者 */ 36 V = FindMinDist( Graph, dist, collected ); 37 if ( V==ERROR ) /* 若這樣的V不存在 */ 38 break; /* 算法結束 */ 39 collected[V] = true; /* 收錄V */ 40 for( W = 0; W < Graph->Nv; W++ ) /* 對圖中的每個頂點W */ 41 /* 若W是V的鄰接點並且未被收錄 */ 42 if ( collected[W]==false && Graph->G[V][W]<INFINITY ) { 43 if ( Graph->G[V][W]<0 ) /* 若有負邊 */ 44 return false; /* 不能正確解決,返回錯誤標記 */ 45 /* 若收錄V使得dist[W]變小 */ 46 if ( dist[V]+Graph->G[V][W] < dist[W] ) { 47 dist[W] = dist[V] + Graph->G[V][W]; /* 更新dist[W] */ 48 path[W] = V; /* 更新S到W的路徑 */ 49 } 50 } 51 } /* while結束*/ 52 return true; /* 算法執行完畢,返回正確標記 */ 53 }
關於找最小dist
①直接掃描所有未收錄頂點-O(V)
T=O(V^2+E) --->對於稠密圖效果好
②將dist存在最小堆中-O(logV)
更新dist(W)的值-O(logV)
T = O(VlogV+ElogV) = O(ElogV) --->對於稠稀疏圖效果好
多源最短路徑問題:求任意兩頂點間的最短路徑
方法一:直接將單源最短路算法調用V遍
T = O(V^3 + E*V) --->對於稀疏圖效果好
方法二:Floyd算法 --->對於稠密圖效果好
T = O(V^3)
Floyd 算法
Dk[i][j] = 路徑{ i -> { l ≤ k } -> j }的最小長度
D0, D1, …, D|V|-1[i][j]即給出了i到j的真正最短距離
最初的D-1是鄰接矩陣
當Dk-1已經完成,遞推到Dk時:
或者k ∉最短路徑{ i -> { l ≤ k } -> j },則Dk = Dk-1
或者k ∈最短路徑{ i -> { l ≤ k } -> j },則該路徑必定由兩段最短路徑組成: Dk[i][j]=Dk-1[i][k]+Dk-1[k][j]

1 /* 鄰接矩陣存儲 - 多源最短路算法 */ 2 3 bool Floyd( MGraph Graph, WeightType D[][MaxVertexNum], Vertex path[][MaxVertexNum] ) 4 { 5 Vertex i, j, k; 6 7 /* 初始化 */ 8 for ( i=0; i<Graph->Nv; i++ ) 9 for( j=0; j<Graph->Nv; j++ ) { 10 D[i][j] = Graph->G[i][j]; 11 path[i][j] = -1; 12 } 13 14 for( k=0; k<Graph->Nv; k++ ) 15 for( i=0; i<Graph->Nv; i++ ) 16 for( j=0; j<Graph->Nv; j++ ) 17 if( D[i][k] + D[k][j] < D[i][j] ) { 18 D[i][j] = D[i][k] + D[k][j]; 19 if ( i==j && D[i][j]<0 ) /* 若發現負值圈 */ 20 return false; /* 不能正確解決,返回錯誤標記 */ 21 path[i][j] = k; 22 } 23 return true; /* 算法執行完畢,返回正確標記 */ 24 }