目录
- /**
- * 内容:
- * 1、拓扑排序
- * 2、逆拓扑
- * 3、i到j之间长度为k的路径
- * 4、i到j之间包含顶点x的路径是否存在
- * 5、如果边是带权的,求解 i 到 j 之间长度最长的路径
- *
- * ①算法思想
- * 1、每一轮把一个入度为0的顶点加入到一个存储结构里面(栈或队列),然后从栈或队列中取出一个入度为0的元素,在图上做一个逻辑上的删除,
- * 删除后减少相关顶点的入度(GetIndegreeX),再次将入度为0的顶点加入到存储结构里,直到逻辑删除完毕所有的顶点。
- * 2、有三种方法实现逆拓扑:
- * 法1:不断逻辑删除出度为0的顶点。(拓扑排序是不断删除入度为0的顶点)
- * 法2:使用DFS,可以想象一下,DFS 一直往深处遍历,直到遍历到这一轮的最后一个节点,说明这
- * 个节点没有下一个了,说明没有出度,只有入度,那么理应是拓扑排序中的最后一个,那么也就是逆拓扑排序中的第一个。
- * 法3:先拓扑排序,最后一步将输出逆置。
- * 3、求i到j之间长度为 length 的路径(拿PathIsSimple()改来的)
- * 4、i到j之间包含顶点 x 的路径(拿PathIsSimple()改来的)
- * 5、如果边是带权的,求解 i 到 j 之间长度最长的路径(拿PathIsSimple()改来的)
- *
- * ②算法设计
- *
- */
- #include <stdio.h>
- #include <iostream>
- #include <cstdio>
- #include <malloc.h>
- #include <cstdlib>
- #define MaxSize 20
- #define INF 999999
-
-
- struct ArcNode{
- int adjvex;
- struct ArcNode *next;
- int weight;
- };
- struct VNode{
- char value;
- struct ArcNode *first;
- };
- struct AdGraph{
- VNode vertices[MaxSize];
- int vexnum,arcnum;
- };
-
- //1、拓扑排序(可以判断有向联通图是否有环,如果不是连通图那么就对几个子部分分别进行判断)
- //求顶点入度:
- //复习:求某个顶点的入度:
- //int GetInDegreeX(AdGraph G,int v){
- // int degree = 0;
- // ArcNode *p;
- // for (int i = 0; i < G.vexnum; ++i) {
- // p = G.vertices[i].first;
- // while(p){
- // if(p -> adjvex == v)
- // degree ++;
- // p = p -> next;
- // }
- // }
- // return degree;
- //}
- //求所有顶点入度:
- void GetAllNodeInDegree(AdGraph G,int *indegree){
- for (int i = 0; i < G.vexnum; ++i) {
- ArcNode *p = G.vertices[i].first;
- while(p){
- indegree[p -> adjvex]++;//入度++
- p = p -> next;
- }
- }
- }
- //Topo函数
- bool Topo(AdGraph G){
- int Stack[MaxSize],top = -1;//保存入度为0的顶点
- int indegree[MaxSize];
- int topoSequece[MaxSize],count = 0,v;//topoSequece[]用来保存拓扑序列,count用来标记存储到哪一块了
- for (int i = 0; i < G.vexnum; ++i) {
- indegree[i] = 0;
- }
- GetAllNodeInDegree(G,indegree);
- //把入度为0的顶点入栈
- for (int i = 0; i < G.vexnum; ++i) {
- if(indegree[i] == 0){
- Stack[++top] = i;
- }
- }
- while(top != -1){
- v = Stack[top--];
- topoSequece[count++] = v;//出栈后加入topo序列
- ArcNode *p = G.vertices[v].first;
- while(p){
- //下面两句也可以这样写:
- //if(!(--indegree[p -> adjvex]){...}
- indegree[p -> adjvex]--;
- if(indegree[p -> adjvex] == 0){
- Stack[++top] = p -> adjvex;
- }
- p = p -> next;
- }
- }
- //打印topoSequence
- for (int i = 0; i < count; ++i) {
- printf("%d ",topoSequece[i]);
- }
- if(count == G.vexnum)//说明拓扑序列里的元素数量和G的顶点数量相同,也就是所有元素都进入过Stack栈,说明所有元素逻辑上都入度为0过
- return true;
- else
- return false;
- }
- //2、逆拓扑
- //方法①:删除出度为0的节点
- //求所有顶点出度:
- void GetAllNodeOutDegree(AdGraph G,int *outdegree){
- for (int i = 0; i < G.vexnum; ++i) {
- ArcNode *p = G.vertices[i].first;
- while(p){
- outdegree[i]++;//出度++
- p = p -> next;
- }
- }
- }
- //逆Topo函数
- bool ReTopo(AdGraph G){
- int Stack[MaxSize],top = -1;//保存入度为0的顶点
- int outdegree[MaxSize];
- int RetopoSequence[MaxSize],count = 0,v;//RetopoSequece[]用来保存拓扑序列,count用来标记存储到哪一块了
- for (int i = 0; i < G.vexnum; ++i) {
- outdegree[i] = 0;
- }
- GetAllNodeOutDegree(G,outdegree);
- //把出度为0的顶点入栈
- for (int i = 0; i < G.vexnum; ++i) {
- if(outdegree[i] == 0){
- Stack[++top] = i;
- }
- }
- //从网中将指向v顶点的顶点的度--
- while(top != -1){
- v = Stack[top--];
- RetopoSequence[count++] = v;//出栈后加入topo序列
- for (int i = 0; i < G.vexnum; ++i) {
- ArcNode *p = G.vertices[i].first;
- while(p){
- //下面两句也可以这样写:
- //if(!(--indegree[p -> adjvex]){...}
- if(p -> adjvex == v)
- outdegree[i]--;
- if(outdegree[i] == 0){
- Stack[++top] = i;
- }
- p = p -> next;
- }
- }
- }
- //将RetopoSequence数组里的元素输出
- for (int i = 0; i < count; ++i) {
- printf("%d ",RetopoSequence[i]);
- }
- if(count == G.vexnum)//说明拓扑序列里的元素数量和G的顶点数量相同,也就是所有元素都进入过Stack栈,说明所有元素逻辑上都入度为0过
- return true;
- else
- return false;
- }
-
- //方法②:使用DFS,DFS元素在出栈前进行输出就是逆拓扑(已知是无环的情况下才能使用DFS)
- //回顾DFS
- void DFS(AdGraph G,int v,int *visited){
- visited[v] = 1;
- printf("%c ",G.vertices[v].value);
- ArcNode *p = G.vertices[v].first;
- while(p){//边链表是断断续续访问完的
- if(!visited[p -> adjvex]){
- DFS(G,p -> adjvex,visited);
- }
- p = p -> next;
- }
- }
- //用DFS实现逆拓扑(已知是无环的情况下)
- void ReverseTopo(AdGraph G,int v,int *visited){
- visited[v] = 1;
- ArcNode *p = G.vertices[v].first;
- while(p){//边链表是断断续续访问完的
- if(!visited[p -> adjvex]){
- ReverseTopo(G,p -> adjvex,visited);
- }
- p = p -> next;
- }
- printf("%d ",v);
- }
-
- //方法③:将拓扑排序序列逆置输出
- //求所有顶点入度:
- void GetAllNodeInDegree(AdGraph G,int *indegree){
- for (int i = 0; i < G.vexnum; ++i) {
- ArcNode *p = G.vertices[i].first;
- while(p){
- indegree[p -> adjvex]++;//入度++
- p = p -> next;
- }
- }
- }
- //逆Topo函数
- bool ReTopo(AdGraph G){
- int Stack[MaxSize],top = -1;//保存入度为0的顶点
- int indegree[MaxSize];
- int RetopoSequence[MaxSize],count = 0,v;//RetopoSequece[]用来保存拓扑序列,count用来标记存储到哪一块了
- for (int i = 0; i < G.vexnum; ++i) {
- indegree[i] = 0;
- }
- GetAllNodeInDegree(G,indegree);
- //把入度为0的顶点入栈
- for (int i = 0; i < G.vexnum; ++i) {
- if(indegree[i] == 0){
- Stack[++top] = i;
- }
- }
- while(top != -1){
- v = Stack[top--];
- RetopoSequence[count++] = v;//出栈后加入topo序列
- ArcNode *p = G.vertices[v].first;
- while(p){
- //下面两句也可以这样写:
- //if(!(--indegree[p -> adjvex]){...}
- indegree[p -> adjvex]--;
- if(indegree[p -> adjvex] == 0){
- Stack[++top] = p -> adjvex;
- }
- p = p -> next;
- }
- }
- //将RetopoSequence数组里的元素逆置输出
- for (int i = count - 1; i >= 0; i--) {
- printf("%d ",RetopoSequence[i]);
- }
- if(count == G.vexnum)//说明拓扑序列里的元素数量和G的顶点数量相同,也就是所有元素都进入过Stack栈,说明所有元素逻辑上都入度为0过
- return true;
- else
- return false;
- }
- //3、求i到j之间长度为length的路径
- //d的初值是0
- void PathIJ_length(AdGraph G,int i,int j,int *path,int d,int *visited,int length){//path是个数组,用于保存i到j的路径,d用来表示存入path数组的下标位置
- path[d] = i;
- if(i == j && d == length){
- for (int k = 0; k <= d; ++k) {//有个=是为了把j这个点的值也输出出来
- printf("%c ",G.vertices[path[k]].value);
- }
- }
- visited[i] = 1;
- ArcNode *p = G.vertices[i].first;
- while(p){//回退来之后,会直接走一下步没有被访问到的点,
- if(!visited[p -> adjvex]){//如果这个点没有被访问过,(保证了不会访问重复顶点)就调用自身
- PathIJ_length(G,p -> adjvex,j,path,d + 1,visited,length);
- }
- p = p -> next;
- }
- visited[i] = 0;
- //如果不置零,只能求出来一个唯一的长度为length的路径,但要求的是所有长度为length的路径。
- //因为某个顶点在这条长度为length的路径中被访问了之后,不一定说不是构成别的长度为length的路径所需要的顶点。
- }
- //4、i到j之间包含顶点x的路径是否存在
- //d的初值是0,刚开始is为0,代表不存在
- void PathIJ_X(AdGraph G,int i,int j,int *path,int d,int *visited,int x,int &is){//path是个数组,用于保存i到j的路径,d用来表示存入path数组的下标位置
- path[d] = i;
- if(i == j){
- for (int k = 0; k <= d; ++k) {
- if(path[k] == x)
- is = 1;
- }
- }
- visited[i] = 1;
- ArcNode *p = G.vertices[i].first;
- while(p){//回退来之后,会直接走一下步没有被访问到的点,
- if(!visited[p -> adjvex]){//如果这个点没有被访问过,(保证了不会访问重复顶点)就调用自身
- PathIJ_X(G,p -> adjvex,j,path,d + 1,visited,x,is);
- }
- p = p -> next;
- }
- visited[i] = 0;//如果不置零,只能求出来一个唯一的路径,但要求的是所有简单路径。
- //因为某个顶点在这条路径中被访问了之后,不一定说不是别的路径所需要的顶点。
- }
- //5、如果边是带权的,求解 i 到 j 之间长度最长的路径
- struct MGraph{
- char vex[MaxSize];
- int weight[MaxSize][MaxSize];
- int vexnum,arcnum;
- };
- //d的初值是0,刚开始is为0,代表不存在
- //有了路径之后,要保存路径的长度,新传入一个参数maxlength
- void PathIJ_MaxLength(MGraph G,int i,int j,int *path,int d,int *visited,int &maxLength,int *maxPath,int count){
- //path是个数组,用于保存i到j的路径,d用来表示存入path数组的下标位置,
- //maxPath是个数组,用来拷贝maxLength最大的那个路径,也就是最长路径,count用来保存maxPath数组有多少个值,初值count=0
- path[d] = i;//path[]存的是路过的顶点的下标
- int sum = 0;//用来保存路径长度,刚开始是0
- if(i == j){
- //保存此条路径的长度
- for (int k = 0; k < d; ++k) {//这边不用再k=d了
- sum += G.weight[path[k]][path[k + 1]];//两顶点之间的长度
- }
- if(maxLength < sum){
- maxLength = sum;
- for (int k = 0; k <= d; ++k) {
- maxPath[count++] = path[k];
- }
- }
- }
- visited[i] = 1;
- for (int k = 0; k < G.vexnum; ++k) {
- if(G.weight[i][k] != 0 && G.weight[i][k] != INF && visited[k] == 0){
- //如果两点间有权值(即有边)并且不是INF并且这个点没有被访问过
- PathIJ_MaxLength(G,k,j,path,d,visited,maxLength,maxPath,count);
- }
- }
- visited[i] = 0;//如果不置零,只能求出来一个唯一的路径,但要求的是所有路径。
- //因为某个顶点在这条最长路径中被访问了之后,不一定说不是构成别的最长路径所需要的顶点。
- }