Loading

简单图论算法

1、图的抽象数据类型

 1 ADT Graph
 2 {
 3   数据:
 4     Graph = (Vertex, Edge)是可以用不同方式存储的图,Vertex是顶点集,
 5     Edge = { <vtx_1, vtx_2> | vtx_1, vtx_2属于Vertex, vtx_1不等于vtx_2, <vtx_1, vtx_2>是连接vtx_1, vtx_2的边 }
 6 
 7   操作:
 8     void InitGraph(Graph &G, int vtxCnt, bool directed);                // 按顶点个数和有向标志构造图
 9     void DestroyGraph(Graph &G);                                        // 清除原有的图G
10     bool IsDirected(Graph &G);                                          // 判断图G是否是有向图
11     int  VertexCount(Graph &G);                                         // 求出并返回图G的顶点数
12     int  EdgeCount(Graph &G);                                           // 求出并返回图G的边数
13     int  FirstAdjoinVertex(Graph &G, int vtx);                          // 返回vtx的第一个邻接顶点,若无邻接点返回-1
14     int  NextAdjoinVertex(Graph &G, int vtx_1, int vtx_2);              // 返回vtx_1的下一个邻接点(相对于vtx_2)
15     void Insert(Graph &G, int vtx_1, int vtx_2);                    // 在图中插入边<vtx_1, vtx_2>
16     void Delete(Graph &G, int vtx_1, int vtx_2);                    // 在图中删除边<vtx_1, vtx_2>
17     bool EdgeExisting(Graph &G, int vtx_1, int vtx_2);                  // 判断边<vtx_1, vtx_2>是否是图的边
18     void Traverse(Graph &G, int vtx, *Visit());                         // 从顶点vtx开始遍历图,Visit()代表遍历策略
19 };
View Code

 

2、图的邻接矩阵表示 

  1 // 数据构造定义:
  2 struct Graph
  3 {
  4     int  vtxCnt;                // 图的顶点数
  5     int  edgCnt;                // 图的边数
  6     bool directed;              // 有向图和无向图的标志
  7     bool **adjMtrx;             // 标识指向邻接顶点的二维数组
  8     bool *visited;              // 顶点是否被访问过的标志
  9 };
 10 
 11 /**
 12 * 下面表示的是无权图的基本操作的实现(带权图的操作实现类似)
 13 **/
 14 /**
 15 * 图的初始化操作
 16 **/
 17 void InitGraph(Graph &G, int vtxCnt, bool directed)
 18 {
 19     G.vtxCnt = vtxCnt;                                  // 初始化顶点数
 20     G.edgCnt = 0;                                       // 初始化边数为0
 21     G.directed = directed;                              // 设置有向图和无向图的标志
 22     G.adjMtrx = new bool[vtxCnt][vtxCnt];               // 为邻接矩阵分配二维内存空间
 23     G.visited = new bool[vtxCnt];                       // 为顶点访问标志数组分配内存空间
 24     for (int i = 0; i < vtxCnt; ++i) {
 25         for (int j = 0; j < vtxCnt; ++j)
 26             G.adjMtrx[i][j] = false;                    // 初始化邻接矩阵
 27         G.visited[i] = false;                           // 初始化顶点访问标志
 28     }
 29 }
 30 
 31 /**
 32 * 清除图的操作
 33 **/
 34 void DestroyGraph(Graph &G)
 35 {
 36     delete[] G.adjMtrx;             // 清除图的标志
 37     delete[] G.visited;             // 清除标志数组
 38     G.vtxCnt = 0;                   // 置顶点数为0
 39     G.edgCnt = 0;                   // 置边数为0
 40 }
 41 
 42 /**
 43 * 图G的顶点数统计
 44 **/
 45 int  VertexCount(Graph &G) { return G.vtxCnt; }
 46 
 47 /**
 48 * 图G的边数统计
 49 **/
 50 int  EdgCnt(Graph &G) { return G.edgCnt; }
 51 
 52 /**
 53 * 判断图G是否是有向图
 54 **/
 55 bool IsDirected(Graph &G) { return G.directed; }
 56 
 57 /**
 58 * 判断边<vtx_1, vtx_2>是否是图G的边,若是,返回true,否则返回false
 59 **/
 60 bool EdgeExisting(Graph &G, int vtx_1, int vtx_2)
 61 {
 62     return G.adjMtrx[vtx_1][vtx_2];
 63 }
 64 
 65 /**
 66 * 在图中插入边<vtx_1, vtx_2>
 67 **/
 68 void Insert(Graph &G, int vtx_1, int vtx_2)
 69 {
 70     if (G.adjMtrx[vtx_1][vtx_2] == false)
 71         ++G.edgCnt;                                  // 要添加的边不存在,边数加1
 72     G.adjMtrx[vtx_1][vtx_2] = true;                  // 添加边<vtx_1, vtx_2>
 73     if (!G.directed)
 74         G.adjMtrx[vtx_2][vtx_1] = true;              // 如果是无向图,处理对称元素
 75 }
 76 
 77 /**
 78 * 在图中删除边<vtx_1, vtx_2>
 79 **/
 80 void Delete(Graph &G, int vtx_1, int vtx_2)
 81 {
 82     if (G.adjMtrx[vtx_1][vtx_2] == true)
 83         --G.vtxCnt;                                 // 要添加的边存在,边数减1
 84     G.adjMtrx[vtx_1][vtx_2] = false;                // 删除边<vtx_1, vtx_2>
 85     if (!G.directed)
 86         G.adjMtrx[vtx_2][vtx_1] = false;            // 如果是无向图,处理对称元素
 87 }
 88 
 89 /**
 90 * 返回vtx的第一个邻接顶点,若vtx没有邻接点,则返回-1
 91 **/
 92 int FirstAdjoinVertex(Graph &G, int vtx)
 93 {
 94     int tempVtx = 0;
 95     while ((tempVtx < G.vtxCnt) && (G.adjMtrx[vtx][tempVtx] == false)){
 96         ++tempVtx;
 97     }
 98     if (tempVtx == G.vtxCnt)
 99         return -1;                                    // 未找到邻接顶点
100     else
101         return tempVtx;                               // 返回邻接顶点
102 }
103 
104 /**
105 * 返回vtx_1的下一个邻接点(相对于vtx_2)
106 **/
107 int NextAdjoinVertex(Graph &G, int vtx_1, int vtx_2)
108 {
109     int tempVtx = vtx_2 + 1;
110     while ((tempVtx < G.vtxCnt) && (G.adjMtrx[vtx_1][tempVtx] == false)){
111         ++tempVtx;
112     }
113     if (tempVtx == G.vtxCnt)
114         return -1;                                    // 未找到邻接顶点
115     else
116         return tempVtx;                               // 返回邻接顶点
117 }
View Code

 

3、图的邻接表表示

  1 // 邻接表的结构声明
  2 struct Graph
  3 {
  4     int  vtxCnt;                    // 图的顶点数
  5     int  edgCnt;                    // 图的边数
  6     bool directed;                  // 有向图/无向图的标志
  7     bool *visited;                  // 顶点是否被访问过的标志
  8     GraphLink *adjMtrx;             // 单链表的表头定义
  9 };
 10 
 11 // 结点抽象类型定义
 12 struct GraphNode
 13 {
 14     int            vtx;                                                      // 顶点
 15     GraphNode    *next;                                                      // 指向边的终端结点的指针
 16     GraphNode(int tempVtx, GraphNode *p) { vtx = tempVtx; next = p; }        // 构造函数
 17 };
 18 typedef GraphNode *GraphLink;
 19 
 20 /**
 21 *    图的初始化操作
 22 **/
 23 void InitGraph(Graph &G, int vtxCnt, bool directed)
 24 {
 25     G.vtxCnt = vtxCnt;                                   // 初始化顶点数
 26     G.edgCnt = 0;                                        // 初始化边数为0
 27     G.directed = directed;                               // 设置有向图和无向图的标志
 28     G.adjMtrx = new GraphLink[vtxCnt];                   // 为邻接矩阵分配内存空间
 29     G.visited = new bool[vtxCnt];                        // 为顶点访问标志数组分配内存空间
 30     for (int i = 0; i < vtxCnt; ++i) {
 31         G.adjMtrx[i] = new GraphNode(i, NULL);           // 初始化单链表表头,结点值为index,指针域为空
 32         G.visited[i] = false;                            // 初始化顶点访问标志
 33     }
 34 }
 35 
 36 /**
 37 * 清除图的操作
 38 **/
 39 void DestroyGraph(Graph &G)
 40 {
 41     for (int i = 0; i < G.vtxCnt; ++i) {                 // 一次销毁各单链表
 42         GraphLink link = G.adjMtrx[i];
 43         while (link) {
 44             GraphLink temp = link;
 45             link = link->next;
 46             delete temp;
 47         }
 48     }
 49     delete[] G.visited;                                  // 清除标志数组
 50     G.vtxCnt = 0;                                        // 置顶点数为0
 51     G.edgCnt = 0;                                        // 置边数为0
 52 }
 53 
 54 /**
 55 * 图G的顶点数统计
 56 **/
 57 int  VertexCount(Graph &G) { return G.vtxCnt; }
 58 
 59 /**
 60 * 图G的边数统计
 61 **/
 62 int  EdgeCount(Graph &G) { return G.edgCnt; }
 63 
 64 /**
 65 * 判断图G是否是有向图
 66 **/
 67 bool IsDirected(Graph &G) { return G.directed; }
 68 
 69 /**
 70 * 判断边<vtx_1, vtx_2>是否是图G的边,若是,返回true,否则返回false
 71 **/
 72 bool EdgeExisting(Graph &G, int vtx_1, int vtx_2)
 73 {
 74     GraphLink link = G.adjMtrx[vtx_1]->next;            // 根据起始点确定链表
 75     while (link) {
 76         if (link->vtx == vtx_2)                         // 边存在
 77             return true;
 78         else
 79             link = link->next;                          // 寻找下一条
 80     }
 81     return false;                                       // 边不存在
 82 }
 83 
 84 /**
 85 * 在图中插入边<vtx_1, vtx_2>
 86 **/
 87 void Insert(Graph &G, int vtx_1, int vtx_2)
 88 {
 89     if (EdgeExisting(G, vtx_1, vtx_2) == false) {
 90         G.adjMtrx[vtx_1]->next = new GraphNode(vtx_2, G.adjMtrx[vtx_1]->next);
 91         if (!G.directed)                                                           // 无向图则处理对称边
 92             G.adjMtrx[vtx_2]->next = new GraphNode(vtx_1, G.adjMtrx[vtx_2]->next);
 93         ++G.edgCnt;                                                   // 边数加1
 94     }
 95 }
 96 
 97 /**
 98 * 在图中删除边<vtx_1, vtx_2>
 99 **/
100 void Delete(Graph &G, int vtx_1, int vtx_2)
101 {
102     GraphLink link = G.adjMtrx[vtx_1];                          // 取链表头
103     while (link->next) {                                        // 寻找待删除的边
104         if (vtx_2 == link->next->vtx) {                         // 找到要删除的边,执行删除操作
105             GraphLink temp = link->next;
106             link->next = temp->next;
107             delete temp;
108             break;
109         }
110         link = link->next;                                      // 指向下一个邻接顶点
111     }
112     if (G.directed == true)                                     // 如果是有向图,则返回
113         return;
114     link = G.adjMtrx[vtx_2];                                    // 取链表头
115     while (link->next) {                                        // 寻找待删除的边
116         if (vtx_1 == link->next->vtx) {                         // 找到要删除的边,执行删除操作
117             GraphLink temp = link->next;
118             link->next = temp->next;
119             delete temp;
120             break;
121         }
122         link = link->next;                                      // 指向下一个邻接顶点
123     }
124 }
125 
126 /**
127 * 返回vtx的第一个邻接顶点,若vtx没有邻接点,则返回-1
128 **/
129 int FirstAdjoinVertex(Graph &G, int vtx)
130 {
131     GraphLink link = G.adjMtrx[vtx]->next;                      // 取链表头
132     if (link)
133         return link->vtx;                                       // 返回第一个邻接顶点
134     else
135         return -1;                                              // 没有邻接顶点,则返回-1
136 }
137 
138 /**
139 * 返回vtx_1的下一个邻接点(相对于vtx_2)
140 **/
141 int  NextAdjoinVertex(Graph &G, int vtx_1, int vtx_2)
142 {
143     GraphLink link = G.adjMtrx[vtx_1]->next;                    // 取链表头
144     while (link) {
145         if (link->vtx == vtx_2 && link->next != NULL)           // 判断当前邻接顶点
146             return link->next->vtx;                             // 返回下一个邻接顶点
147         else
148             link = link->next;                                  // 指向下一个邻接顶点
149     }
150     return -1;                                                  // 未找到,返回-1
151 }
View Code

 

4、图的遍历

  1 /**
  2 * 图的广度优先遍历
  3 **/
  4 void BreadthFirstSearch(Graph &G, int vtx)
  5 {
  6     Queue queue;                                                    // 定义循环队列
  7     InitQueue(queue);                                               // 初始化队列
  8     if (G.visited[vtx] == false)                                    // 是否没有访问过
  9         G.visited[vtx] = true;                                      // 置访问标志,可插入顶点访问操作
 10     EnQueue(queue, vtx);                                            // 顶点入队列
 11     while (!IsEmpty(queue)) {                                       /* 循环直到队列为空 */
 12         int curVtx = DeQueue(queue);                                // 队列元素出队
 13         for (int temp = FirstAdjoinVertex(G, curVtx); temp != -1; temp = NextAdjoinVertex(G, curVtx, temp)){
 14             if (G.visited[temp] == false) {                       // 是否没有访问过
 15                 G.visited[temp] = true;                          // 置访问标志
 16                 EnQueue(queue, temp);                               // 刚访问过的顶点元素入队
 17             }
 18         }
 19     }
 20     DestroyQueue(queue);
 21 }
 22 
 23 /**
 24 * 图的深度优先遍历(递归实现)
 25 **/
 26 void DepthFirstTraverse(Graph &G, int vtx)
 27 {
 28     G.visited[vtx] = true;                                                     // 置访问标志
 29     for (int tempVtx = FirstAdjoinVertex(G, vtx); tempVtx != -1; ++tempVtx) {  // 依次访问顶点vtx的邻接顶点
 30         if (G.visited[tempVtx] == false)                                       // 若顶点未访问,则递归调用
 31             DepthFirstTraverse(G, tempVtx);
 32     }
 33 }
 34 
 35 /**
 36  * 图的深度优先遍历的非递归算法
 37  * 思路:根据对图的深度优先遍历的规则,可以以不同的非递归算法来实现图的深度优先搜索遍历,
 38  * 以下列举三种方法:
 39  **/
 40 /**
 41  * 1、采用一个找出顶点vtx的一个未被访问过的顶点的操作:NotVisitAdjoinVertex(G, vtx),
 42  *       以每个顶点最先搜索到的邻接点在先的顺序做深度优先搜索遍历来实现
 43  **/
 44 void DepthFirstSearch_1(Graph &G, int vtx)
 45 {
 46     Stack stack;                                             // 从顶点vtx出发深度优先遍历图G
 47     InitStack(stack, MAXSIZE);                               // 初始化栈stack
 48     if (G.visited[vtx] == false) {                           // 若vtx未访问过,则访问并标记
 49         G.visited[vtx] = true;
 50         cout << vtx << " -> ";
 51     }
 52     Push(stack, -1);                                         // 以“-1”作为遍历结束的标志
 53     Push(stack, vtx);                                        // 刚刚访问过的顶点入栈
 54     int preVtx = vtx;                                        // preVtx指向刚刚访问过的顶点
 55     while(preVtx != -1) {                                    // 循环直到遇到结束标志
 56         int tempVtx = NotVisitAdjoinVertex(G, preVtx);       // 寻找preVtx的一个未访问过的临界点tempVtx
 57         if(tempVtx != -1) {                                  // 若找到tempVtx则访问并对其做标记
 58             G.visited[tempVtx] = true;
 59             cout << tempVtx << " -> ";
 60             Push(stack, tempVtx);                            // 将tempVtx入栈
 61             preVtx = tempVtx;                                // 继续寻找tempVtx未访问过的邻接点
 62         } else {
 63             preVtx = Pop(stack);        // 若当前顶点的所有邻接点都已经访问过,则退出栈顶顶点继续循环搜索
 64         }
 65     }
 66 }
 67 
 68 /**
 69  * 2、利用对顶点vtx_1找相对于邻接点vtx_2的下一个邻接点的操作:NextAdjoinVertex(G, vtx_1, vtx_2),
 70  *    以每个顶点最后搜索到的邻接点在先的顺序做深度优先搜索遍历来实现
 71  **/
 72 void DepthFirstSearch_2(Graph &G, int vtx)
 73 {
 74     Stack stack;                                             // 定义栈
 75     InitStack(stack, MAXSIZE);                               // 初始化栈stack
 76     Push(stack, vtx);                                        // 起始顶点入栈
 77     while(!IsEmpty(stack)) {                                 // 循环至栈stack空
 78         int currVtx = Pop(stack);                            // currVtx为栈顶顶点
 79         if(G.visited[currVtx] == false) {                    // 若currVtx未被访问过,访问并标记
 80             G.visited[currVtx] = true;
 81             cout << currVtx << " -> ";
 82         }
 83         /* 找出刚访问过的顶点currVtx的所有邻接点,并将未被访问过的邻接点依次入栈 */
 84         for(int temp = FirstAdjoinVertex(G, currVtx); temp != -1; temp = NextAdjoinVertex(G, currVtx, temp)) {
 85             if(G.visited[temp] == false)
 86                 Push(stack, temp);
 87         }
 88     }
 89 }
 90 
 91 /**
 92  * 3、利用对顶点vtx_1找相对于邻接点vtx_2的下一个邻接点的操作:NextAdjoinVertex(G, vtx_1, vtx_2),
 93  *    以每个顶点最先搜索到的邻接点在先的顺序做深度优先搜索遍历来实现
 94  **/
 95 void DepthFirstSearch_3(Graph &G, int vtx)
 96 {
 97     Stack stack;                                             // 定义顺序栈
 98     InitStack(stack, MAXSIZE);                               // 初始化顺序栈
 99     if(G.visited[vtx] == false) {                            // 若顶点vtx未被访问过,访问并标记之
100         G.visited[vtx] = true;
101         cout << vtx << " -> ";
102     }
103     Push(stack, vtx);                                        // 初始顶点入栈
104     while(!IsEmpty(stack)) {                                 // 循环遍历直到栈stack空
105         int preVtx = GetTop(stack);                          // 读取出栈顶顶点并赋给preVtx
106         int flag = 1;                 // 当前正在检查的顶点preVtx是否有未被访问的邻接点的标志,“1”表示都被访问过
107         for(int temp = FirstAdjoinVertex(G, preVtx); preVtx != -1; temp = NextAdjoinVertex(G, preVtx, temp)) {
108             if(G.visited[temp] == false) {                   // 若找到temp的未被访问过的邻接顶点temp,访问并标记
109                 G.visited[temp] = true;
110                 cout << temp << " -> ";
111                 Push(stack, temp);                           // 将stack入栈
112                 flag = 0;                                    // 置还有未被访问过的邻接点标志“0”
113                 break;                                       // 继续对刚刚访问的顶点temp查找是否有未被访问过的邻接点
114             }
115         }
116         if(flag == 1)                                        // 若栈顶顶点的所有邻接点都已经访问过,则将其退栈
117             Pop(stack);
118     }
119 }
120 
121 /**
122 * 寻找图中从顶点vtx_1到顶点vtx_2的简单路径
123 * 基本思路:
124 *    对依附于顶点vtx_1的每条边<vtx_1, temp_1>,寻找从顶点temp_1到顶点vtx_2的一条简单路径,而且不经过vtx_1,
125 *    考虑依附于顶点temp_1的边<temp_1, temp_2>,寻找从顶点temp_2到顶点vtx_2的一条简单路径,并且不经过顶点vtx_1和
126 *    顶点temp_1,如此下去,直到找到解为止,所以这个问题可以利用深度优先遍历实现。
127 *    从顶点vtx_1出发做深度优先遍历,如果遇到顶点vtx_2,回溯就可以得到从顶点vtx_1到顶点vtx_2的一条路径。那
128 *    如何保证这是一条简单路径,方法是:维护一条路径,依次记录深度优先遍历过程中访问过的顶点;在深度优先遍历过程中,如果
129 *    顶点的所有邻接顶点都被访问过,仍然未能到达目标顶点vtx_2,则将此顶点从路径中删除;到达目标顶点后,就可以得到一条简单路径
130 **/
131 void SimplePath(Graph &G, int vtx_1, int vtx_2)
132 {
133     G.visited[vtx_1] = true;                                            // 设置访问标志
134     Append(path, vtx_1);                                                // 将当前顶点加入路径
135     for (int temp = FirstAdjoinVertex(G, vtx_1); temp != -1; temp = NextAdjoinVertex(G, vtx_1, temp)){
136         if (temp == vtx_2) {                                            // 查找成功
137             Append(path, vtx_2);
138             return;
139         }
140         if (!G.visited(temp))                                           // 递归调用,深度优先遍历
141             SimplePath(G, temp, vtx_2);
142     }
143     Delete(path, vtx_1);                                                // 删除路径上最后一个顶点
144 }
145 
146 /**
147 * 非连通图的遍历
148 **/
149 void DepthFirstTraverse(Graph &G, int vtx)
150 {
151     for (vtx = 0; vtx < G.vtxCnt; ++vtx) {                              // 只需把每个顶点作为起点进行深度优先遍历即可
152         G.visited[vtx] = true;
153         for (int temp = FirstAdjoinVertex(G, vtx); temp != -1; tmep = NextAdjoinVertex(G, vtx, temp)){
154             if (G.visited[temp] == false)
155                 DepthFirstTraverse(G, temp);
156         }
157     }
158 }
View Code

 

5、有向图的传递闭包

 1 /**
 2  * 求解有向图的传递闭包 --- Warshall算法
 3  **/
 4  // G表示图,access[][]表示可达矩阵
 5 void Warshall(Graph &G, bool **access) {
 6     // 初始化可达矩阵
 7     for(int i = 0; i < G.vtxCnt; ++i) {        
 8         for(int j = 0; j < G.vtxCnt; ++j)
 9             access[i][j] = G.adjMtrx[i * G.vtxCnt + j];        
10 
11     for(int k = 0; k < G.vtxCnt; ++k) {                        // 顶点循环
12         for(int i = 0; i < G.vtxCnt; ++i) {                    // 行循环
13             for(int j = 0; j < G.vtxCnt; ++j) {                // 列循环
14                 if(access[i][k] && access[k][j])
15                     access[i][j] = 1;                         // 置 i->j 可达标志true
16             }
17         }
18     }
19 }
View Code

 

6、拓扑排序

 1 /**
 2 * 拓扑排序
 3 **/
 4 void TopologicalSort(Graph &G, int *topoSeq)
 5 {
 6     Queue queue;
 7     InitQueue(queue);
 8     int *inDegrees = new int[G.vtxCnt];                         // 记录每个顶点的入度
 9     for (int vtx = 0; vtx < G.vtxCnt; ++vtx)                    // 初始化顶点入度
10         inDegrees[vtx] = 0;
11     for (int vtx = 0; vtx < G.vtxCnt; ++vtx) {                  // 遍历图得到顶点输入边数
12         for (int temp = FirstAdjoinVertex(G, vtx); temp != -1; temp = NextAdjoinVertex(G, vtx, temp)) {
13             ++inDegrees[temp];
14         }
15     }
16     for (int vtx = 0; vtx < G.vtxCnt; ++vtx)
17         if (inDegrees[vtx] == 0)
18             EnQueue(queue, vtx);                                // 源点入源点队列
19     for (int index = 0; !IsEmpty(queue); ++index) {             // 开始拓扑排序
20         int srcVtx = DeQueue(queue);
21         topoSeq[index] = srcVtx;                                // 从源点队列中取元素如拓扑队列
22         for (int temp = FirstAdjoinVertex(G, srcVtx); temp != -1; temp = NextAdjoinVertex(G, srcVtx, temp)) {
23             --inDegrees[temp];                                  // 源点的邻接顶点入度减1
24             if (inDegrees[temp] == 0)
25                 EnQueue(queue, temp);                           // 若成为新源点,入源点队列
26         }
27     }
28     DetroyQueue(queue);
29 }
View Code

 

7、图的最小生成树

 1 // 带权边的结构定义
 2 struct Edge
 3 {
 4     int     begin;                // 边的起点
 5     int     end;                 // 边的终点
 6     double  weight;               // 边的权重
 7 };
 8 
 9 /**
10 * Prim算法(针对图的邻接矩阵表示)
11 **/
12 void Prim(Graph &G, Edge &MST)
13 {
14     Edge *edge = new Edge[G.vtxCnt];                                // 用于维护MST顶点到非MST顶点的最短距离
15     int vtx = 0;                                                    // 假设从顶点0开始求MST
16     for (int i = 0; i < G.vtxCnt; ++i) {                            // 初始化MST顶点到非MST顶点的最短距离数组
17         edge[i].begin = vtx;
18         edge[i].end = i;
19         edge[i].weight = G.adjMtrx[vtx][i];
20     }
21 
22     edge[vtx].weight = 0.0;                                         // MST内顶点距离设为0
23     int edgCnt = 0;                                                 // 已经找到的MST边数
24     for (int i = 1; i < G.vtxCnt; ++i) {                            // 需要找n-1条边
25         int minIndex = MinEdgeIndex(edge, G.vtxCnt);                // 调用函数求最短边的索引值
26         MST[edgCnt++] = edge[minIndex];                             // 保存最短边到MST数组
27         vtx = edge[minIndex].end;                                   // 当前边的终点加入到MST数组
28         edge[vtx].weight = 0.0;                                     // MST内顶点距离设为0
29         for (int j = 0; j < G.vtxCnt; ++j) {                        // 更新MST顶点到非MST顶点的最短距离
30             if (G.adjMtrx[vtx][j] < edge[j].weight) {               // 找到更短边
31                 edge[j].begin = vtx;                                // 更新起始顶点
32                 edge[j].weight = G.adjMtrx[vtx][j];                 // 更新权重
33             }
34         }
35     }
36     delete [] edge;
37 }
38 
39 /**
40 * 选取权重最小所对应的边
41 **/
42 int MinEdgeIndex(Edge *edge, int edgCnt)
43 {
44     int minIndex = -1;                                              // 用于记录最短边序号
45     double minWeight = INFINITE;                                    // 最短边权重初始化为无穷大
46     for (int i = 0; i < edgCnt; ++i) {                              // 遍历所有的边
47         if (edge[i].weight == 0)
48             continue;                                               // 权重为0,表明已属于MST,则跳过
49         if (edge[i].weight < minWeight) {                           // 比较权重
50             minWeight = edge[i].weight;                             // 记录权重当前最小的边
51             minIndex = i;
52         }
53     }
54     return minIndex;                                                // 返回权重最小所对应的边的序号
55 }
56 
57 /**
58 * Kruskal算法(针对图的邻接表表示)
59 * *********环的判断应用了等价类和并查集的思想 **********
60 **/
61 void Kruskal(Graph &G, Edge *MST)
62 {
63     /* 堆排序 */
64     Heap heap;
65     InitHeap(heap);
66     for (int i = 0; i < G.vtxCnt; ++i) {                                                // 利用堆插入对边排序
67         GraphLink link = G.adjMtrx[i];
68         while (link) {
69             if (link->vtx > i) {                                                        //*** 对称边只取一条
70                 Edge *edge = new Edge(i, link->vtx, link->weight);                      // 构造边,存放边的序列指针
71                 HeapInsert(heap, (int) edge);                                           // 堆插入并调整
72             }
73             link = link->next;
74         }
75     }
76 
77     /* 求解最小生成树 */
78     int *vtxConSet = new int[G.vtxCnt];                                                 // 用于标识顶点连通的数组
79     for (int i = 0; i < G.vtxCnt; ++i)                                                  // 初始化用于标识顶点连通性的集合
80         vtxConSet[i] = i;
81     int edgCnt = 0;                                                                     // 记录MST中的边数
82     int addr = -1;                                                                      // 存放边的地址
83     while ((!IsEmpty(heap)) && (edgCnt < G.vtxCnt - 1)) {                               // MST中边数(n-1)不够且堆不空
84         HeapRemove(heap, &addr);                                                        // 取出权值最小的边
85         if (vtxConSet[((Edge *) addr)->begin] == vtxConSet[((Edge *) addr)->end])
86             continue;                                                                   // 若构成环则将边舍弃
87         MST[edgCnt++] = (Edge *) addr;                                                  // 保存有效的MST组成边
88         int lastVtx = vtxConSet[((Edge *) addr)->end];                                  // 合并新边两个端点所在的顶点集合
89         for (int in = 0; in < G.vtxCnt; ++in) {
90             if (vtxConSet[in] == lastVtx)
91                 vtxConSet[in] = vtxConSet[((Edge *) addr)->begin];
92         }
93     }
94     DestroyHeap(heap);
95     delete [] vtxConSet;
96 }
View Code

 

8、图的最短路径问题

  1 /**
  2 * 单源最短路径问题 --- Dijkstra算法
  3 **/
  4 void DijkstraShortestPathTree(Graph &G, int vtx, Edge *SPT)
  5 {
  6     Edge *path = new Edge[G.vtxCnt];                                            // 维护从源点到各顶点的最短距离
  7     for (int i = 0; i < G.vtxCnt; ++i) {                                        // 初始化源点到各顶点的距离数组
  8         path[i].begin = vtx;
  9         path[i].end = i;
 10         path[i].weight = G.adjMtrx[vtx][i];
 11     }
 12 
 13     path[vtx].weight = 0.0;                                                     // SPT内顶点距离设为0
 14     int edgCnt = 0;                                                             // 已经找到的SPT的边数
 15     for (int count = 1; count < G.vtxCnt; ++count) {                            // 需要找n-1条边
 16         int minIndex = MinEdgeIndex(path, G.vtxCnt);                            // 获取最短路径的索引值
 17         SPT[edgCnt++] = path[minIndex];                                         // 保存最短路径到结果数组
 18         vtx = path[minIndex].end;                                               // 当前加入SPT的顶点
 19         for (int j = 0; j < G.vtxCnt; ++j) {                                    // 更新从源点到各顶点的最短距离
 20             if (path[vtx].weight + G.adjMtrx[vtx][j] < path[j].weight) {
 21                 path[j].begin = vtx;                                            // 记录边的起始顶点
 22                 path[j].weight = path[j].weight + G.adjMtrx[vtx][j];
 23             }
 24         }
 25         path[vtx].weight = 0.0;                                                 // SPT内的顶点不再参与比较
 26     }
 27     delete [] path;
 28 }
 29 
 30 /**
 31  * 带负权值边的单源最短路径 --- Bellman-Ford算法(缺点:不能处理带负环的图)(稍作修改即可求解最长路径)
 32  *   步骤:
 33  *        1、初始化一个先进先出队列,将源点放入
 34  *        2、一次取出队列中的顶点
 35  *        3、检查从新取出顶点发出的边;若能使由源点到某个顶点的路径长度有所减少,则保存此路径,
 36  *           用以取代从源点到该目的顶点的原有路径,并将目的顶点放入队列中
 37  *        4、重复步骤(2)、(3),直到队列空,就得到了单源最短路径
 38  **/
 39 void Bellman-Ford(Graph &G, int vtx, Edge *SPT)
 40 {
 41     Queue queue;
 42     InitQueue(queue);
 43     for(int i = 0; i < G.vtxCnt; ++i) {                                         // 初始化SPT数组
 44         SPT[i].begin = vtx;
 45         SPT[i].end = i;
 46         SPT[i].weight = INFINITE;
 47     }
 48     SPT[vtx] = 0.0;                                                             // 源点路径设为0
 49     EnQueue(queue, vtx);                                                        // 源点入队列
 50     while(!IsEmpty(queue)) {                                                    // 循环直到队列空
 51         int temp = DeQueue(queue);                                              // 元素出队列
 52         GraphLink link = G.adjMtrx[temp]->next;                                 // 取边链表地址
 53         while(link) {                                                           // 若找到更短路径则更新
 54             if(SPT[temp].weight + link->weight < SPT[link->vtx].weight) {
 55                 SPT[link->vtx].begin = temp;
 56                 SPT[link->vtx].weight = SPT[temp].weight + link->weight;
 57                 EnQueue(queue, link->vtx);
 58             }
 59             link = link->next;
 60         }
 61     }
 62     DetroyQueue(queue);
 63 }
 64 
 65 /**
 66 * 全源最短路径问题 --- Floyd算法
 67 * 或者对每个顶点调用Dijkstra算法
 68 **/
 69 // dist为边值矩阵,path为路径矩阵
 70 void FloydShortestPathTree(Graph &G, double **dist, int **path)
 71 {
 72     /* 边值矩阵和路径矩阵初始化 */
 73     for (int i = 0; i < G.vtxCnt; ++i) {
 74         for (int j = 0; j < G.vtxCnt; ++j) {
 75             dist[i][j] = G.adjMtrx[i * G.vtxCnt + j];
 76             path[i][j] = i;
 77         }
 78     }
 79     for (int i = 0; i < G.vtxCnt; ++i)                  // 顶点到自身的距离定位0
 80         dist[i][i] = 0.0;
 81 
 82     /* 求各个顶点对之间的最短路径 */
 83     for (int k = 0; k < G.vtxCnt; ++k) {                // 顶点循环
 84         for (int i = 0; i < G.vtxCnt; ++i) {            // 起点循环
 85             for (int j = 0; j < G.vtxCnt; ++j) {        // 终点循环
 86                 // 判断是否存在更短的路径,有,则边值矩阵和路径矩阵更新;dist[in][post]得出的是in到post的最短路径 
 87                 if (dist[i][j] > dist[i][k] + dist[k][j]) {
 88                     dist[i][j] = dist[i][k] + dist[k][j];
 89                     path[i][j] = path[k][j];
 90                 }
 91             }
 92         }
 93     }
 94 }
 95 
 96 /**
 97  * 获取图经过Floyd算法处理后任意两点间的路径序列
 98  **/
 99 void Path(Path *path, int vtx_1, int vtx_2)
100 {
101     Stack stack;
102     InitStack(stack);                             // 初始化栈
103     Push(stack, vtx_2);                           // 终点进栈
104     int temp = path[vtx_1][vtx_2];                // 获取终点的前驱结点作为当前结点
105     while(temp != vtx_1) {                        // 循环搜索直到当前结点是起始点
106         Push(stack, temp);                        // 当前结点入栈
107         temp = path[vtx_1][temp];                 // 继续寻找当前结点的前驱结点
108     }
109     Push(stack, temp);                            // 起始点点入栈
110 
111     while(!IsEmpty(stack))                        // 输出路径序列
112         cout << Pop(stack) << " -> ";
113 }
View Code

Ok哒,哈哈~

posted @ 2014-08-01 00:38  dai.sp  阅读(410)  评论(0)    收藏  举报