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 };
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 }
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 }
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 }
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 }
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 }
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 }
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 }
Ok哒,哈哈~