数据结构之图
图(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 }