这部分为了快速入坑参考了浙大的课程,邓俊辉那个由于自己 C++
基础太拉胯了就换了个简单的
www。后面还有一些细节的地方懒得搞了,反正写了这文章也图一乐
2333333,以后再润色了(
「re:
从零开始的带学数据结构㈡」图的一点基础理解
图的相关定义
概论
线性表和树本身是图的特殊情形。线性表和树表示的是一对一和一对多的关系,而图表示的是多对多的关系。包含:
一组顶点(Vertex)
 
一组边(Edge)
其中:
边是顶点对, (v, w) ∈ E,其中 v, w ∈ V;
有向边 <v, w> 表示从 v 指向 w 的边(单行线);
不考虑自回路
 
简单路径:不经过相同的顶点。
邻接:顶点和顶点
关联:顶点和边
ADT

表示方法
邻接矩阵
G [N] [N],N 个顶点从 0 到 N-1 编号。
- 注意:二维数组的顺序是 
A [X] [Y]。分别代表 X 位置和 Y
位置。 
其中 G [I] [J] = 1,若 <vi, vj>是 G
中的边;否则是 0.
直观。方便找邻接点、边、计算度(出度、入度)。但面对稀疏图的时候会浪费空间。
邻接表
G [N] 是指针数组,对应矩阵每行一个链表,只存非 0 元素。
节约稀疏图的空间、便于计算出度。但不便检查顶点间的边、也不便计算入度。
图的遍历
广度优先搜索(BFS)
一套矩阵图的 BFS
实现。本质上是个队列的操作,按顺序压进去也就相当于按照广度优先的顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   |  void BFS(Vertex S) {          queue<Vertex> Q;     Vertex Temp;
           cout << MGraph->Elements[S] << endl;          MGraph->Visited[S] = true;          Q.push(S);
      while(!Q.empty())     {         Temp = Q.front();         Q.pop();
          for(int i = 0; i < MGraph->Vertex_Amount; i++)           {                          if(MGraph->WeightContainer[Temp][i] != INFINITY && !MGraph->Visited[i])             {                                  cout << MGraph->Elements[i] << endl;                                  MGraph->Visited[i] = true;                                  Q.push(i);             }         }     } }
 
  | 
 
深度优先搜索(DFS)
伪代码实现:
1 2 3 4 5 6 7 8
   | void DFS(Vertex V) {     V.visited = true;     for(iterator in adjvexs of V)     {         DFS(V.adjvex);     } }
   | 
 
最短路径问题
在网络中,求两个不同顶点之间的所有路径中,边的权值之和最小的一条路径。这条路径(源点
- 终点)的路径就是最短路径。
分为:
- 单源:对从某固定源点出发,到其他顶点
 
- 多源:任意两顶点。
 
单源带权图最短路径问题——Dijkstra
算法(视频理解)
前提
给定两个数组 Distance 和 Previous
来表示目标点到不同点的距离,以及达到这个路径需要走的前一个节点。
过程
初始化:先利用 flag 标记源点(下例表示为
V0),代表收入到最短路线集合里面;并且计算 V0 到邻接点的距离,存入
Distance 数组内,上一个元素显然是 V0,故存入 Previous 中。
 
循环开始:每次找未标记的节点里面距离出发点最近(Distance
最小)的节点。标记、收录到最短路线集合里面。
计算它到邻接点的距离,如果端点 P 和邻接点 A 之间满足: \[
Dist[A] < Dist[P] + Edge<P, A>
\] 那么就把邻接点的 Distance[A] 和 Previous[A] 更新。(松弛)
每步取最优的操作是贪心算法思想的体现。
 
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
   |           Vertex FindMinDistance(GNode* G, int distance[], bool collected[])     {         Vertex MinPosition;         int MinDist = INFINITY;                           for(int i = 0; i < G->Vertex_Amount; i++)         {             if(collected[i] == false && distance[i] < MinDist)             {                                  MinDist = distance[i];                   MinPosition = i;                 }         }
          if(MinDist < INFINITY)             return MinPosition;         else             return -1;       }
      void Dijkstra(GNode* G, int distance[], int previous[], Vertex S)     {         bool collected[MaxVertexNum] = {false};         previous = {-1};         distance = {INFINITY};                               distance[S] = 0;         collected[S] = true;
          while(1)         {                          Vertex V = FindMinDistance(G, distance, collected);             if(V==-1)                 break;                                       collected[V] = true;                          for(Vertex W = 0; W < G->Vertex_Amount; W++)             {                    if(collected[W] == false && G->WeightContainer[V][W] < INFINITY)                 {                     if(distance[V] + G->WeightContainer[V][W] < distance[W])                     {                         distance[W] = distance[V] + G->WeightContainer[V][W];                         previous[W] = V;                     }                 }             }         }     }
 
  | 
 
多源最短路算法——Floyd 算法
前提
调用 V 次 Dijkstra
之类的算法也可以暴力解决多源最短路算法。它的时间复杂度是
O(V^3 + V*E),但只在稀疏图可以实现 Floyd 算法的程度,达到
O(V^3) 的水平。
过程
代码实现
最小生成树
前提
树:无回路,边比顶点少一个。
生成:包含所有顶点,边都在图内。
权重和最小。
最小生成树存在,图一定联通。
算法分析
贪心算法可以在这个问题应用:
- 每一步取最优结果——代表权重最小的边
 
- 约束情况:只能用图中的边、正好用掉 V-1 条、不能有回路
 
方法 1:Prim 算法
Prim
算法的基本思路是:从一个根节点开始,慢慢地建立起一棵树。每次选权重最小的来生成。如果最后节点数量不够,就说明图不连通,生成失败。
伪代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | void Prim() { 	MST = {s}; //最小生成树 	while(1) 	{	 		V = 最小 dist; 		if 不存在,break; 		 		将 V 收录到 MST:dist[V] = 0 		 		for(V 的每个邻接点 W) 			if(dist[W]!=0) 			{ 				if E(V,W) < dist[W] 					dist[W] = E(V,W) 					parent[W] = V; 			} 	} 	if MST 顶点不够 V 个 		error(生成树不存在) }
   |