首页 > 代码库 > Graph Theory Algorithms

Graph Theory Algorithms

 

  1 /**  2  * 图论  3  **/  4   5  /***************************** 图的抽象数据类型 ************************************/  6 ADT Graph  7 {  8     数据:  9     Graph = (Vertex, Edge)是可以用不同方式存储的图,Vertex是顶点集, 10     Edge = {<vertex_1, vertex_2> | vertex_1, vertex_2属于Vertex,vertex_1不等于vertex_2,<vertex_1, vertex_2>表示连接vertex_1和vertext_2的边} 11  12     操作: 13     void InitGraph(Graph &graph, int vertextCount, bool directed);// 按顶点个数vertexCount和有向标志directed构造图graph 14     void DestroyGraph(Graph &graph);                                     // 清楚原有的图graph 15     bool IsDirected(Graph &graph);                                       // 判断图graph是否是有向图 16     int  VertexCount(Graph &graph);                                      // 求出并返回图graph的顶点数 17     int  EdgeCount(Graph &graph);                                        // 求出并返回图graph的边数 18     int  FirstAdjoinVertex(Graph &graph, Vertex vertex);          // 返回vertex的第一个邻接顶点,若vertex没有邻接点,则返回-1 19     int  NextAdjoinVertex(Graph &graph, Vertex vertex_1, Vertex vertex_2);  // 返回vertex_1的下一个邻接点(相对于vertex_2) 20     void Insert(Graph &graph, Vertex vertex_1, Vertex vertex_2);            // 在图中插入边<vertex_1, vertex_2> 21     void Delete(Graph &graph, Vertex vertex_1, Vertex vertex_2);            // 在图中删除边<vertex_1, vertex_2> 22     bool EdgeExisting(Graph &graph, Vertex vertex_1, Vertex vertex_2);   // 判断边<vertex_1, vertex_2>是否是图graph的边,                                                                                       若是,返回true,否则返回false 23     void Traverse(Graph &graph, Vertex vertex, *Visit());// 从顶点vertex开始遍历graph,对每个顶点调用函数Visit(),Visit()因遍历方法而异 24 }     25  26 struct Graph 27 { 28     int  vertextCount;             // 图的顶点数 29     int  EdgeCount;                // 图的边数 30     bool directed;                 // 有向图和无向图的标志 31     bool *adjoinMatrix;            // 指向邻接矩阵的指针 32     bool *visited;                 // 顶点是否被访问过的标志 33 }; 34  35 /****************************** 图的邻接矩阵表示 ***********************************/ 36 /** 37  * 下面表示的是无权图的基本操作的实现(带权图的操作实现类似) 38  **/ 39 /** 40  *    图的初始化操作 41  **/ 42 void InitGraph(Graph &graph, int vertextCount, bool directed) 43 { 44     graph.vertextCount = vertextCount;                                // 初始化顶点数 45     graph.EdgeCount = 0;                                              // 初始化边数为0 46     graph.directed = directed;                                        // 设置有向图和无向图的标志 47     graph.adjoinMatrix = new bool[vertextCount * vertextCount];       // 为邻接矩阵分配内存空间 48     graph.visited = new bool[vertextCount];                           // 为顶点访问标志数组分配内存空间 49     for(int out = 0; out < vertextCount; ++out) {         50         for(int in = 0; in < vertextCount; ++in) 51             graph.adjoinMatrix[out * vertextCount + in] = false;      // 初始化邻接矩阵 52         graph.visited[out] = false;                                   // 初始化顶点访问标志 53     } 54 } 55  56 /** 57  * 清除图的操作 58  **/ 59 void DestroyGraph(Graph &graph) 60 { 61     delete [] graph.adjoinMatrix;        // 清除图的标志 62     delete [] graph.visited;             // 清除标志数组 63     graph.vertextCount = 0;              // 置顶点数为0 64     graph.EdgeCount = 0;                 // 置边数为0 65 } 66  67 /** 68  * 图graph的顶点数统计 69  **/ 70 int  VertexCount(Graph &graph) { return graph.vertextCount; } 71  72 /** 73  * 图graph的边数统计 74  **/ 75 int  EdgeCount(Graph &graph) { return graph.EdgeCount; } 76  77 /** 78  * 判断图graph是否是有向图 79  **/ 80 bool IsDirected(Graph &graph) { return graph.directed; } 81  82 /** 83  * 判断边<vertex_1, vertex_2>是否是图graph的边,若是,返回true,否则返回false 84  **/ 85 bool EdgeExisting(Graph &graph, Vertex vertex_1, Vertex vertex_2) 86 { 87     return graph.adjoinMatrix[vertex_1 * graph.vertextCount + vertex_2]; 88 } 89  90 /** 91  * 在图中插入边<vertex_1, vertex_2> 92  **/ 93 void Insert(Graph &graph, Vertex vertex_1, Vertex vertex_2) 94 { 95     if(graph.adjoinMatrix[vertex_1 * graph.vertextCount + vertex_2] == false) 96         ++graph.EdgeCount;                                                              // 要添加的边不存在,边数加1 97     graph.adjoinMatrix[vertex_1 * graph.vertextCount + vertex_2] = true;                // 添加边<vertex_1, vertex_2> 98     if(!graph.directed) 99         graph.adjoinMatrix[vertex_2 * graph.vertextCount + vertex_1] = true;            // 如果是无向图,处理对称元素100 }101 102 /**103  * 在图中删除边<vertex_1, vertex_2>104  **/105 void Delete(Graph &graph, Vertex vertex_1, Vertex vertex_2)106 {107     if(graph.adjoinMatrix[vertex_1 * graph.vertextCount + vertex_2] == true)108         --graph.vertextCount;                                                            // 要添加的边存在,边数减1109     graph.adjoinMatrix[vertex_1 * graph.vertextCount + vertex_2] = false;                // 删除边<vertex_1, vertex_2>110     if(!graph.directed)111         graph.adjoinMatrix[vertex_2 * graph.vertextCount + vertex_1] = false;            // 如果是无向图,处理对称元素112 }113 114 /**115  * 返回vertex的第一个邻接顶点,若vertex没有邻接点,则返回-1116  **/117 int  FirstAdjoinVertex(Graph &graph, Vertex vertex)118 {119     int tempVertex = 0;120     while((tempVertex < graph.vertextCount) && (graph.adjoinMatrix[vertex * graph.vertextCount + tempVertex] == false)) {121         ++tempVertex;122     }123     if(tempVertex == graph.vertextCount)124         return -1;                                // 未找到邻接顶点125     else 126         return tempVertex;                        // 返回邻接顶点127 }    128 129 /**130  * 返回vertex_1的下一个邻接点(相对于vertex_2)131  **/132 int  NextAdjoinVertex(Graph &graph, Vertex vertex_1, Vertex vertex_2)133 {134     int tempVertex = vertex_2 + 1;135     while((tempVertex < graph.vertextCount) && (graph.adjoinMatrix[vertex * graph.vertextCount + tempVertex] == false)) {136         ++tempVertex;137     }138     if(tempVertex == graph.vertextCount)139         return -1;                                // 未找到邻接顶点140     else141         return tempVertex;                        // 返回邻接顶点142 }143 144 145 /***************************** 图的邻接表表示表示 ***********************************/146 // 结点抽象类型定义147 struct GraphNode148 {149     int vertex;                                                                            // 顶点150     GraphNode *next;                                                                       // 指向边的终端结点的指针151     GraphNode(int tempVertex, GraphNode *pointer) { vertex = tempVertex; next = pointer; } // 构造函数152 };153 154 typedef GraphNode *GraphLink;155 156 // 邻接表的结构声明157 struct Graph158 {159     int  vertextCount;                // 图的顶点数160     int  edgeCount;                   // 图的边数161     bool directed;                    // 有向图/无向图的标志162     bool *visited;                    // 顶点是否被访问过的标志163     GraphLink adjoinMatrix;           // 单链表的表头定义164 };165 166 /**167  *    图的初始化操作168  **/169 void InitGraph(Graph &graph, int vertextCount, bool directed)170 {171     graph.vertextCount = vertextCount;                           // 初始化顶点数172     graph.EdgeCount = 0;                                         // 初始化边数为0173     graph.directed = directed;                                   // 设置有向图和无向图的标志174     graph.adjoinMatrix = new GraphLink[vertextCount];            // 为邻接矩阵分配内存空间175     graph.visited = new bool[vertextCount];                      // 为顶点访问标志数组分配内存空间176     for(int index = 0; index < vertextCount; ++index) {        177         graph.adjoinMatrix[index] = new GraphNode(index, NULL);  // 初始化单链表表头,结点值为index,指针域为空178         graph.visited[index] = false;                            // 初始化顶点访问标志179     }180 }181 182 /**183  * 清除图的操作184  **/185 void DestroyGraph(Graph &graph)186 {187     for(int index = 0; index < graph.vertextCount; ++index) {     // 一次销毁各单链表188         GraphLink link = graph.adjoinMatrix[index];189         while(link) {190             GraphLink tempLink = link;191             link = link->next;192             delete tempLink;193         }194     }195     delete [] graph.visited;            // 清除标志数组196     graph.vertextCount = 0;             // 置顶点数为0197     graph.EdgeCount = 0;                // 置边数为0198 }199 200 /**201  * 图graph的顶点数统计202  **/203 int  VertexCount(Graph &graph) { return graph.vertextCount; }204 205 /**206  * 图graph的边数统计207  **/208 int  EdgeCount(Graph &graph) { return graph.EdgeCount; }209 210 /**211  * 判断图graph是否是有向图212  **/213 bool IsDirected(Graph &graph) { return graph.directed; }214 215 /**216  * 判断边<vertex_1, vertex_2>是否是图graph的边,若是,返回true,否则返回false217  **/218 bool EdgeExisting(Graph &graph, Vertex vertex_1, Vertex vertex_2)219 {220     GraphLink link = graph.adjoinMatrix[vertex_1]->next;            // 根据起始点确定链表221     while(link) {222         if(link->vertex == vertex_2)                                // 边存在223             return true;224         else225             link = link->next;                                      // 寻找下一条226     }227     return false;                                                   // 边不存在228 }229 230 /**231  * 在图中插入边<vertex_1, vertex_2>232  **/233 void Insert(Graph &graph, Vertex vertex_1, Vertex vertex_2)234 {235     if(EdgeExisting(graph, vertex_1, vertex_2) == false) {      // 没有判断要添加的边是否存在236         graph.adjoinMatrix[vertex_1]->next = new GraphNode(vertex_2, graph.adjoinMatrix[vertex_1]->next); 237         if(!graph.directed)238             graph.adjoinMatrix[vertex_2]->next = new GraphNode(vertex_1, graph.adjoinMatrix[vertex_2]->next); // 若是无向图239         ++graph.EdgeCount;                           // 边数加1    240     }                                                                               241 }242 243 /**244  * 在图中删除边<vertex_1, vertex_2>245  **/246 void Delete(Graph &graph, Vertex vertex_1, Vertex vertex_2)247 {248     GraphLink link = graph.adjoinMatrix[vertex_1];                // 取链表头249     while(link->next) {                                           // 寻找待删除的边250         if(vertex_2 == link->next->vertex) {                      // 找到要删除的边,执行删除操作251             GraphLink tempLink = link->next;252             link->next = tempLink->next;253             delete tempLink;254             break;255         }256         link = link->next;                                        // 指向下一个邻接顶点257     }258     if(graph.directed == true)                                    // 如果是有向图,则返回259         return ;260     link = graph.adjoinMatrix[vertex_2];                          // 去链表头261     while(link->next) {                                           // 寻找待删除的边262         if(vertex_1 == link->next->vertex) {                      // 找到要删除的边,执行删除操作263             GraphLink tempLink = link->next;264             link->next = tempLink->next;265             delete tempLink;266             break;267         }268         link = link->next;                                        // 指向下一个邻接顶点269     }270 }271 272 /**273  * 返回vertex的第一个邻接顶点,若vertex没有邻接点,则返回-1274  **/275 int FirstAdjoinVertex(Graph &graph, int vertex)276 {277     GraphLink link = graph.adjoinMatrix[vertex]->next;            // 取链表头278     if(link)279         return link->vertex;                                      // 返回第一个邻接顶点280     else281         return -1;                                                // 没有邻接顶点,则返回-1282 }283 284 /**285  * 返回vertex_1的下一个邻接点(相对于vertex_2)286  **/287 int  NextAdjoinVertex(Graph &graph, Vertex vertex_1, Vertex vertex_2)288 {289     GraphLink link = graph.adjoinMatrix[vertex_1]->next;          // 取链表头290     while(link) {291         if(link->vertex == vertex_2 && link->next != NULL)        // 判断当前邻接顶点292             return link->next->vertext;                           // 返回下一个邻接顶点293         else294             link = link->next;                                    // 指向下一个邻接顶点295     }296     return -1;                                                    // 未找到,返回-1297 }298 299 /**300  * 图的深度优先遍历301  **/302 void DepthFirstTraverse(Graph &graph, int vertex) 303 {304     graph.visited[vertex] = true;                                                    // 置访问标志305     for(int tempVertex = FirstAdjoinVertex(graph); tempVertex != -1; ++tempVertex) { // 依次访问顶点vertex的邻接顶点306         if(graph.visited[tempVertex] == false)                                       // 若顶点未访问,则递归调用307             DepthFirstTraverse(graph, tempVertex);308     }309 }310 311 /**312  * 图的广度优先遍历313  **/314 void BreadthFirstSearch(Graph &graph, Vertex vertex)315 {316     CircleQueue circleQueue;                                    // 定义循环队列317     InitQueue(circleQueue);                                     // 初始化循环队列318     if(graph.visited[vertex] == false)                          // 是否没有访问过319         graph.visited[vertex] = true;                           // 置访问标志,可插入顶点访问操作320     EnQueue(circleQueue, vertex);                               // 顶点入队列321     while(!IsEmpty(circleQueue)) {                              /* 循环直到队列为空 */322         Vertex currentVertex;323         DeQueue(circleQueue, &currentVertex);                   // 队列元素出队324         for(Vertex tempVertex = FirstAdjoinVertex(graph, currentVertex); tempVertex != -1; tempVertex = NextAdjoinVertex(graph, currentVertex, tempVertex)) {325             if(graph.visited[tempVertex] == false) {            // 是否没有访问过326                 graph.visited[tempVertex] = true;               // 置访问标志327                 EnQueue(circleQueue, tempVertex);               // 刚访问过的顶点元素入队328             }329         }330     }331     DestroyGraph(circleQueue);332 }333 334 /**335  * 寻找图中从顶点vertex_1到顶点vertex_2的简单路径336  * 基本思路:337  *         对依附于顶点vertex_1的每条边<vertex_1, temp_1>,寻找从顶点temp_1到顶点vertex_2的一条简单路径,而且不经过vertex_1,338  *        考虑依附于顶点temp_1的边<temp_1, temp_2>,寻找从顶点temp_2到顶点vertex_2的一条简单路径,并且不经过顶点vertex_1和339  *        顶点temp_1,如此下去,直到找到解为止,所以这个问题可以利用深度优先遍历实现。340  *        从顶点vertex_1出发做深度优先遍历,如果遇到顶点vertex_2,回溯就可以得到从顶点vertex_1到顶点vertex_2的一条路径。那341  *        如何保证这是一条简单路径,方法是:维护一条路径,依次记录深度优先遍历过程中访问过的顶点;在深度优先遍历过程中,如果342  *        顶点的所有邻接顶点都被访问过,仍然未能到达目标顶点vertex_2,则将此顶点从路径中删除;到达目标顶点后,就可以得到一条简单路径343  **/344 void SimplePath(Graph &graph, Vertex vertex_1, Vertex vertex_2)345 {346     graph.visited[vertex_1] = true;                                        // 设置访问标志347     Append(path, vertex_1);                                                // 将当前顶点加入路径348     for(Vertex tempVertex = FirstAdjoinVertex(graph, vertex_1); tempVertex != -1; tempVertex = NextAdjoinVertex(graph, vertex_1, tempVertex)) {349         if(tempVertex == vertex_2) {                                       // 查找成功350             Append(path, vertex_2);351             return ;352         }353         if(!graph.visited(tempVertex))                                     // 递归调用354             SimplePath(graph, tempVertex, vertex_2);355     }356     Delete(path, vertex_1);                                                // 删除路径上最后一个顶点357 }358 359 /**360  * 非连通图的遍历361  **/362 void DepthFirstTraverse(Graph &graph, Vertex vertext)363 {364     for(vertext = 0; vertext < graph.vertextCount; ++vertext) {     // 只需把每个顶点作为起点进行深度优先遍历即可365         graph.visited[vertext] = true;366         for(int temp = FirstAdjoinVertex(graph, vertext); temp != -1; tmep = NextAdjoinVertex(graph, vertext, temp)) {367             if(graph.visited[temp] == false)368                 DepthFirstTraverse(graph, temp);369         }370     }371 }372 373 /**374  * 拓扑排序375  **/376 void TopologicalSort(Graph &graph, int *topologicalSequence)377 {378     CircleQueue circleQueue;379     InitQueue(circleQueue);380     int *inDegrees = new int[graph.vertextCount];                        // 记录每个顶点的入度381     for(int vertex = 0; vertex < graph.vertextCount; ++vertex)           // 初始化顶点入度382         inDegrees[vertex] = 0;    383     for(int vertex = 0; vertex < graph.vertextCount; ++vertex) {         // 遍历图得到顶点输入边数384         for(int tempVertex = FirstAdjoinVertex(graph, vertext); tempVertex != -1; tempVertex = NextAdjoinVertex(graph, vertex, tempVertex)) {385             ++inDegrees[tempVertex];386         }387     }388     for(int vertex = 0; vertex < graph.vertextCount; ++vertex)389         if(inDegrees[vertex] == 0)390             EnQueue(circleQueue, vertex);                                // 源点入源点队列391     for(int index = 0; !IsEmpty(circleQueue); ++index) {                 // 开始拓扑排序392         int outVertex;393         DeQueue(circleQueue, &outVertex);394         topologicalSequence[index] = outVertex;                          // 从源点队列中取元素如拓扑队列395         for(int tempVertex = FirstAdjoinVertex(graph, outVertex); tempVertex != -1; tempVertex = NextAdjoinVertex(graph, outVertex, tempVertex)) {396             --inDegrees[tempVertex];                                     // 源点的邻接顶点入度减1397             if(inDegrees[tempVertex] == 0)398                 EnQueue(circleQueue, tempVertex);                        // 若成为新源点,入源点队列399         }400     }401     DetroyQueue(circleQueue);402 }

 

 

Ok哒,哈哈~