天天看点

如何判断图中是否有环,如果有输出每条环方法一:用ALGraph举例方法二:用MGraph举例方法一的完整代码方法二的完整代码

【题目】试写一个求有向图G中所有简单回路的算法

【思路】

方法一:

DFS搜索,直到搜索到已经遍历到的结点–>说明:找到了回路

判断该回路是否已经搜索到了(重复吗?)

不重复,放入结果集

方法二:

DFS搜索,只往结点编号大的结点搜索–>这样就不会重复寻找

搜索到和第一个位置上的结点相同–>搜索到了路径

找到后放入结果集

【测试数据】123456对应ABCDEF

方法一:用ALGraph举例

【结果】

如何判断图中是否有环,如果有输出每条环方法一:用ALGraph举例方法二:用MGraph举例方法一的完整代码方法二的完整代码

【答案】

VertexType cycles[maxSize][MAX_VERTEX_NUM+1]; //存放所有回路

int path[MAX_VERTEX_NUM+1]; //路径,前面定义过

int visit[MAX_VERTEX_NUM]; //访问标记,前面定义过

int pathnum=0; //已发现的路径个数,前面已经定义过

Status ExistCycle(ALGraph G, int start, int end) { // [start,end)

    int i,j,k,e;

    int len;

    int flag=0;

    len = end-start;

    for (i=0; i<pathnum; i++) {

        if (strlen(cycles[i])==len) { //长度一样

            //[start,end) ?= cycles[i]-->判断两个回路是否相同

            flag=0; //找到了0个一样的

            for (j=start; j<end; j++) {

                e = path[j];

                //在cycles[i]中找e

                for (k=0; cycles[i][k]!='\0'; k++) {

                    if (cycles[i][k]==G.vers[e].data) flag++; //找到了

                }

            }

            if (flag==len) return TRUE; //找到了len一样的元素-->完全相同

        }

    }

    return FALSE; //不存在

}

void FindAllCycle(ALGraph G, int v, int k) {

    ArcNode *p;

    int i,j;

    int start,nextadj;

    visit[v]=1;

    path[k]=v;

    // 从v的邻边开始走

    for (p=G.vers[v].firstarc; p; p=p->next) {

        nextadj = p->adjV; //下一个结点

        if (visit[nextadj]) { //已经访问过了-->找到了回路

            //找到这条回路的起始点

            for (i=0; i<k; i++) {

                if (path[i]==nextadj) {

                    start=i;

                }

            }

            if (!ExistCycle(G, start, k+1)) { //这个回路没有重复

                for (i=start, j=0; i<=k; i++,j++) {

                    cycles[pathnum][j] = G.vers[ path[i] ].data;

                }

                cycles[pathnum][j]='\0';

                pathnum++;

            }

        } else { //没有访问过,继续访问

            FindAllCycle(G, nextadj, k+1);

        }

    }

    // 回溯

    visit[v]=0;

    path[k]=0;

}

void GetAllCycle(ALGraph G) {

    int i;

    for (i=0; i<G.vernum; i++) visit[i]=0; //访问标记初始化

    pathnum=0; //路径个数初始化

    for (i=0; i<G.vernum; i++) {

        if (visit[i]==0) FindAllCycle(G, i, 0);

    }

}

方法二:用MGraph举例

【结果】

如何判断图中是否有环,如果有输出每条环方法一:用ALGraph举例方法二:用MGraph举例方法一的完整代码方法二的完整代码

int visit[MAX_VERTEX_NUM]; //访问标记

int p[MAX_VERTEX_NUM]; //暂存路径

int k;

int path[MAX_VERTEX_NUM+1][MAX_VERTEX_NUM+1]; //存储找到的路径

    // path[0][0] 存放路径的总个数

    // path[1][0] 存放第一条路径的长度 path[1][1]开始存放第一条的结点

void GetAllCycle_DFSUntil(MGraph G, int i) {

    int u,j;

    visit[i] = 1; //标记i已访问

    //用u遍历i的邻边

    for (u=0; u<G.vexnum; u++) {

        if (G.arcs[i][u].adj==0) continue; //i到u没有边:退出

        // u>p[1] --> i的邻边>路径的第一个结点编号 --> 只往结点号大的地方走,不要往回走 --> 往回走就会找重复的

        if (u>p[1] && visit[u]==0) {

            p[++k] = u; //记到路径上

            GetAllCycle_DFSUntil(G, u); //往u继续走

        }

        // i的邻边u==路径的第一个结点 --> 找到了一条回路

        if (u==p[1]) {

            path[0][0]++; //总路径的个数

            path[ path[0][0] ][0] = k; // 路径长度

            for (j=1; j<=k; j++) //将求得的路径存入路径数组

                path[ path[0][0] ][j] = p[j]; //回路的终点即起点,不存入数组

        }

    }

    visit[ p[k] ] = 0; //返回上一个顶点

    k--;

}

void GetAllCycle(MGraph G) {

    int i,j;

    // path初始化

    for (i=0; i<MAX_VERTEX_NUM; i++) {

        for (j=0; j<MAX_VERTEX_NUM; j++)

            path[i][j]=0;

    }

    // 访问状态初始化:初始为WHITE

    for (i=0; i<=G.vexnum; i++) visit[i]=0;

    // 开始找

    for (i=0; i<=G.vexnum; i++) {

        k=1; //路径的第一个结点

        p[k]=i; //第一个结点的编号为i

        GetAllCycle_DFSUntil(G, i);

    }

}

方法一的完整代码

【完整代码】

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#ifndef BASE

#define BASE

#define TRUE 1

#define FALSE 0

#define OK 1

#define ERROR 0

#define INFEASIBLE -1

#define OVERFLOW -2

typedef int Status;

typedef int bool;

#endif

#define VertexType char //点类型

#define VRType int //边类型

#define maxSize 100

void Visit(VertexType e) {

    printf("%c", e);

}

#define MAX_VERTEX_NUM 20

typedef enum{DG, UDG} GraphKind;

typedef struct ArcNode{

    int adjV; //边指向的顶点

    VRType weight; //权重

    struct ArcNode *next;

}ArcNode; //边

typedef struct VNode{

    VertexType data;

    ArcNode *firstarc;

}VNode, AdjList[MAX_VERTEX_NUM]; //顶点

typedef struct{

    GraphKind kind;

    int vernum,arcnum;

    AdjList vers; 

}ALGraph;

Status InitGraph_AL(ALGraph *pG) { //初始化

    int i;

    pG->arcnum = 0;

    pG->vernum = 0;

    for (i=0; i<MAX_VERTEX_NUM; ++i)

        pG->vers[i].firstarc = NULL; //VC++6.0中指针初始化为0xcccccccc

    return OK;

}

int LocateVex_AL(ALGraph G, VertexType e) { //定位值为e的元素下标

    int i;

    for (i=0; i<G.vernum; ++i) {

        if (G.vers[i].data == e) {

            return i;

        }

    }

    return -1;

}

Status CreateDG_AL(ALGraph *pG) { //创建有向图的邻接表

    //输入规则:顶点数目->弧的数目->各顶点的信息->各条弧的信息

    int i,a,b;

    char tmp[MAX_VERTEX_NUM];

    char h,t;

    ArcNode *p, *q;

    InitGraph_AL(pG); //VC++6.0中指针初始化为0xcccccccc,如果不将指针初始化为NULL,会出错

    //图的类型

    pG->kind = DG;

    //顶点数目

    scanf("%d", &i); if (i<0) return ERROR;

    pG->vernum = i;

    //弧的数目

    scanf("%d", &i); if (i<0) return ERROR;

    pG->arcnum = i;

    //各顶点信息

    scanf("%s", tmp);

    for (i=0; i<pG->vernum; ++i) pG->vers[i].data=tmp[i];

    //弧的信息

    for (i=0; i<pG->arcnum; ++i) {

        scanf("%s", tmp);

        h = tmp[0]; t = tmp[2];

        a = LocateVex_AL(*pG, h);

        b = LocateVex_AL(*pG, t);

        if (a<0 || b<0) return ERROR;

        p = (ArcNode *)malloc(sizeof(ArcNode)); if (!p) exit(OVERFLOW);

        p->adjV=b;p->next=NULL;

        if (pG->vers[a].firstarc) { //已经有边了

            for (q = pG->vers[a].firstarc; q->next; q=q->next) ; //找到最后一条

            q->next = p;

        } else { //第一条边

            pG->vers[a].firstarc = p;

        }

    }

    return OK;

}

VertexType cycles[maxSize][MAX_VERTEX_NUM+1]; //存放所有回路

int path[MAX_VERTEX_NUM+1]; //路径,前面定义过

int visit[MAX_VERTEX_NUM]; //访问标记,前面定义过

int pathnum=0; //已发现的路径个数,前面已经定义过

Status ExistCycle(ALGraph G, int start, int end) { // [start,end)

    int i,j,k,e;

    int len;

    int flag=0;

    len = end-start;

    for (i=0; i<pathnum; i++) {

        if (strlen(cycles[i])==len) { //长度一样

            //[start,end) ?= cycles[i]-->判断两个回路是否相同

            flag=0; //找到了0个一样的

            for (j=start; j<end; j++) {

                e = path[j];

                //在cycles[i]中找e

                for (k=0; cycles[i][k]!='\0'; k++) {

                    if (cycles[i][k]==G.vers[e].data) flag++; //找到了

                }

            }

            if (flag==len) return TRUE; //找到了len一样的元素-->完全相同

        }

    }

    return FALSE; //不存在

}

void FindAllCycle(ALGraph G, int v, int k) {

    ArcNode *p;

    int i,j;

    int start,nextadj;

    visit[v]=1;

    path[k]=v;

    // 从v的邻边开始走

    for (p=G.vers[v].firstarc; p; p=p->next) {

        nextadj = p->adjV; //下一个结点

        if (visit[nextadj]) { //已经访问过了-->找到了回路

            //找到这条回路的起始点

            for (i=0; i<k; i++) {

                if (path[i]==nextadj) {

                    start=i;

                }

            }

            if (!ExistCycle(G, start, k+1)) { //这个回路没有重复

                for (i=start, j=0; i<=k; i++,j++) {

                    cycles[pathnum][j] = G.vers[ path[i] ].data;

                }

                cycles[pathnum][j]='\0';

                pathnum++;

            }

        } else { //没有访问过,继续访问

            FindAllCycle(G, nextadj, k+1);

        }

    }

    // 回溯

    visit[v]=0;

    path[k]=0;

}

void GetAllCycle(ALGraph G) {

    int i;

    for (i=0; i<G.vernum; i++) visit[i]=0; //访问标记初始化

    pathnum=0; //路径个数初始化

    for (i=0; i<G.vernum; i++) {

        if (visit[i]==0) FindAllCycle(G, i, 0);

    }

}

int main() {

    int i;

    ALGraph G;

    CreateDG_AL(&G);

    GetAllCycle(G);

    printf("发现%d条简单回路\n", pathnum);

    for (i=0; i<pathnum; i++) {

        printf("%s\n", cycles[i]);

    }

    return 0;

}

方法二的完整代码

#include<stdio.h>

#include<stdlib.h>

#ifndef BASE

#define BASE

#define TRUE 1

#define FALSE 0

#define OK 1

#define ERROR 0

#define INFEASIBLE -1

#define OVERFLOW -2

typedef int Status;

typedef int bool;

#endif

typedef char VertexType;//点类型

typedef int VRType; //边类型

void Visit(VertexType e) {

    printf("%c", e);

}

#define maxSize 20

#define MAX_VERTEX_NUM 20

typedef enum{UDG, DG} GraphKind;

typedef struct ArcCell{

    VRType adj;

}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

typedef struct{

    VertexType vexs[MAX_VERTEX_NUM]; //顶点

    AdjMatrix arcs; //弧的信息

    int vexnum,arcnum;

    GraphKind kind;

}MGraph;

Status CreateG_MG(MGraph *pG) {

    int i,j,w;

    char tmp[maxSize];

    scanf("%d", &i); if (i<0) return ERROR;

    pG->vexnum = i;

    scanf("%d", &i); if (i<0) return ERROR;

    pG->arcnum = i;

    scanf("%s", tmp);

    for (i=0; i<pG->vexnum; i++) pG->vexs[i]=tmp[i];

    for (i=0; i<pG->vexnum; i++) {

        for (j=0; j<pG->vexnum; j++) {

            scanf("%d", &w);

            pG->arcs[i][j].adj = w;

        }

    }

    return OK;

}

int visit[MAX_VERTEX_NUM]; //访问标记

int p[MAX_VERTEX_NUM]; //暂存路径

int k;

int path[MAX_VERTEX_NUM+1][MAX_VERTEX_NUM+1]; //存储找到的路径

    // path[0][0] 存放路径的总个数

    // path[1][0] 存放第一条路径的长度 path[1][1]开始存放第一条的结点

void GetAllCycle_DFSUntil(MGraph G, int i) {

    int u,j;

    visit[i] = 1; //标记i已访问

    //用u遍历i的邻边

    for (u=0; u<G.vexnum; u++) {

        if (G.arcs[i][u].adj==0) continue; //i到u没有边:退出

        // u>p[1] --> i的邻边>路径的第一个结点编号 --> 只往结点号大的地方走,不要往回走 --> 往回走就会找重复的

        if (u>p[1] && visit[u]==0) {

            p[++k] = u; //记到路径上

            GetAllCycle_DFSUntil(G, u); //往u继续走

        }

        // i的邻边u==路径的第一个结点 --> 找到了一条回路

        if (u==p[1]) {

            path[0][0]++; //总路径的个数

            path[ path[0][0] ][0] = k; // 路径长度

            for (j=1; j<=k; j++) //将求得的路径存入路径数组

                path[ path[0][0] ][j] = p[j]; //回路的终点即起点,不存入数组

        }

    }

    visit[ p[k] ] = 0; //返回上一个顶点

    k--;

}

void GetAllCycle(MGraph G) {

    int i,j;

    // path初始化

    for (i=0; i<MAX_VERTEX_NUM; i++) {

        for (j=0; j<MAX_VERTEX_NUM; j++)

            path[i][j]=0;

    }

    // 访问状态初始化:初始为WHITE

    for (i=0; i<=G.vexnum; i++) visit[i]=0;

    // 开始找

    for (i=0; i<=G.vexnum; i++) {

        k=1; //路径的第一个结点

        p[k]=i; //第一个结点的编号为i

        GetAllCycle_DFSUntil(G, i);

    }

}

int main() {

    MGraph G;

    int pathsNum; //路径的个数

    int pathlen; //该路径的长度

    int i,j;

    int node;

    G.kind = DG;

    CreateG_MG(&G);

    GetAllCycle(G);

    //输出所有回路

    pathsNum = path[0][0];

    for (i=1; i<=pathsNum; i++) { //一共有i条回路

        pathlen = path[i][0]; //第i条路径有pathlen个结点

        for (j=1; j<=pathlen; j++) {

            node = path[i][j];

            Visit(G.vexs[node]);

        }

        printf("\n");

    }

    return 0;

}

继续阅读