數據結構學習筆記05圖 (鄰接矩陣 鄰接表-->BFS DFS、最短路徑)


數據結構之圖

圖(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 } 
sj5_0 圖的鄰接矩陣

 

二.鄰接表

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 }
sj5_1 圖的鄰接表

 

三.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 }
View Code

 

有權圖(無論是否有向):按照遞增的順序找出到各個頂點的最短路

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 }
View Code

關於找最小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 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM