数据结构--图的DFS和BFS算法题总结

有向图的创建、求度、遍历

题目描述

从键盘接收有向图的顶点集,弧集,创建有向图,并完成下列任务:

(1)计算结点的出度、入度以及度;

(2) 从第一个顶点出发,求一个深度优先遍历序列;

(3) 从第一个顶点顶点出发,求一个广度优先遍历序列。

注意:以用户输入各个顶点的顺序为顶点的序号。

      在深度和广度优先遍历中,优先选择序号小的顶点。

输入

第一行输入两个整数,以空格隔开,分别代表图的顶点数n和弧数e。(顶点个数<=20)
第二行依次输入顶点值,类型为字符,中间不用间隔符。
接下去有e行,每行为两个字符 uv(中间没有间隔符),表示一条弧<u,v>。

输出

连续输出n行,依次为各个结点的出度和入度,格式为【顶点w 出度值 入度值 度值】,四者间用空格隔开。
接下去的一行,输出深度优先遍历顶点序列(顶点间无间隔符)。

最后一行,输出广度优先遍历顶点序列(顶点间无间隔符)。

样例输入 Copy

5 7
ABCDE
AB
AE
BC
CD
DA
DB
EC
样例输出 Copy

A 2 1 3
B 1 2 3
C 1 2 3
D 2 1 3
E 1 1 2
ABCDE
ABECD

#include <stdio.h>
#include <stdlib.h>
#define MAXVEX 200
#define INFINITY 32767
typedef struct {
    int arcs[MAXVEX][MAXVEX];
    char vex[MAXVEX];
    int vexnum;
    int arcnum;
}AdiMatrix;
typedef struct Queue {
    char array[MAXVEX];
    int rear;
    int front;
}Queue;
void Init(Queue *q) {
    q->front = q->rear = MAXVEX;
}
void EnterQueue(Queue *q, char v0) {
    q->rear = (q->rear + 1) % MAXVEX;
    q->array[q->rear] = v0;
}
int Empty(Queue q) {
    if (q.rear == q.front) {
        return 1;
    } else {
        return 0;
    }
}
void DeleteQueue(Queue *q, char *v) {
    q->front = (q->front + 1) % MAXVEX;
    *v = q->array[q->front];
}

int LocateVex(AdiMatrix *g, char v) {
    for (int i = 1; i <= g->vexnum; i++) {
        if (g->vex[i] == v) {
            return i;
        }
    }
    return 0;
}
void Create(AdiMatrix *g) {
    scanf("%d %d", &g->vexnum, &g->arcnum);
    getchar();
    for (int i = 1; i <= g->vexnum; i++) {
        for (int j = 1; j <= g->vexnum; j++) {
            g->arcs[i][j] = 0;
        }
    }
    for (int i = 1; i <= g->vexnum; i++) {
        scanf("%c", &g->vex[i]);
    }
    for (int k = 1; k <= g->arcnum; k++) {
        char vex1, vex2;
        getchar();
        scanf("%c", &vex1);
        int i = LocateVex(g, vex1);
        scanf("%c", &vex2);
        int j = LocateVex(g, vex2);
        g->arcs[i][j] = 1;
    }
    
}
void Output(AdiMatrix *g) {
    for (int i = 1; i <= g->vexnum; i++) {
        int count1 = 0, count2 = 0;
        printf("%c ", g->vex[i]);
        for (int j = 1; j <= g->vexnum; j++) {
            if (g->arcs[i][j] == 1) {
                count1++;
            }
        }
        for (int k = 1; k <= g->vexnum; k++) {
            if (g->arcs[k][i] == 1) {
                count2++;
            }
        }
        printf("%d %d %d", count1, count2, count1 + count2);
        printf("\n");
    }
}
int visited[MAXVEX] = {0};
char FirstAdjVex(AdiMatrix *g, char v0) {
    int i;
    for (i = 1; i <= g->vexnum; i++) {
        if (g->vex[i] == v0) {
            break;
        }
    }
    for (int j = 1; j <= g->vexnum; j++) {
        if (g->arcs[i][j] == 1 && visited[g->vex[j]] == 0) {
            return g->vex[j];
        }
    }
    return -1;
}
int NextAdjVex(AdiMatrix *g, char v0, char w) {
    int i;
    for (i = 1; i <= g->vexnum; i++) {
        if (g->vex[i] == v0) {
            break;
        }
    }
    for (int j = 1; j <= g->vexnum; j++) {
        if (g->arcs[i][j] == 1 && g->vex[j] != w  && visited[g->vex[j]] == 0) {
            return g->vex[j];
        }
    }
    return -1;
}
void DFS(AdiMatrix *g, char v0) {
    printf("%c", v0);
    visited[v0] = 1;
    int w = FirstAdjVex(g, v0);
    while (w != -1) {
        if (!visited[w]) {
            DFS(g, w);
            //下面这个语句的执行是在回溯的时候进行的
            //系统内部的递归工作栈会把这一层的结果,变量保存,当我们递推结束
            //会开始回溯过程
            //在回溯过程中,我们要继续搜索其他顶点序列的其他路径
            //比如这个题,画个图容易看出是从A开始搜索,A->B,B->C, C->D,D后面没有字符了,这个时候开始回溯,返回到C这一层,C后面除了D没有其他路径了,而D我们已经搜索过,继续回溯,返回B这一层,B这一层与C那一层同理,继续回溯,返回A这一层,发现A还可以到E,下面这条语句就是为什么我们回溯过程可以发现A还可以有一条路径是A->E的。这一层我们的递归工作栈会保存刚才递推时的变量的值(v0 = A, w = B)。
            w = NextAdjVex(g, v0, w);
        }
    }
}
void TraverseG(AdiMatrix *g) {
    for (int i = 1; i <= g->vexnum; i++) {
        if (!visited[g->vex[i]]) {
            DFS(g, g->vex[i]);
        }
    }
}
void BFS(AdiMatrix *g, int v0) {
    printf("%c", v0);
    visited[v0] = 1;
    Queue q;
    Init(&q);
    EnterQueue(&q, v0);
    int w;
    while (!Empty(q)) {
        char v;
        DeleteQueue(&q, &v);
        w = FirstAdjVex(g, v);
        while (w != -1) {
            if (!visited[w]) {
                printf("%c", w);
                visited[w] = 1;
                EnterQueue(&q, w);
            }
            w = NextAdjVex(g, v, w);
        }
    }
}
void TraverseG2(AdiMatrix *g) {
    for (int i = 1; i <= g->vexnum; i++) {
        if (!visited[g->vex[i]]) {
            BFS(g, g->vex[i]);
        }
    }
}
int main(int argc, const char * argv[]) {
    AdiMatrix *g;
    g = (AdiMatrix *)malloc(sizeof(AdiMatrix));
    Create(g);
    Output(g);
    TraverseG(g);
    for (int i = 0; i < MAXVEX; i++) {
        visited[i] = 0;
    }
    printf("\n");
    TraverseG2(g);
    printf("\n");
    return 0;
}

求解连通分量个数

题目描述

从键盘接收图的顶点集,关系集,创建无向图。
第一行依次输入图的顶点个数n,关系个数k,以空格隔开。顶点个数<=20
第二行依次输入顶点值,类型为字符。
接下去有k行,每行为两个字符 u 和 v,表示节点u 和 v 连通。格式为【uv】,中间不用空格间隔。
计算连通分量个数并输出。
输出一个整数,表示连通分量个数。
样例输入 Copy

6 7
ABCDEF
AB
AE
BC
CD
DA
DB
EC
样例输出 Copy

2

这个题在上一个题的基础上增加一个计数变量即可
思路:首先,我们在对图进行遍历的时候,不管是使用DFS或者BFS,如果这个图是连通图,那么只需要走一遍DFS或BFS就可以全部遍历完。

for (int i = 1; i <= g->vexnum; i++) {
        if (!visited[g->vex[i]]) {
            (*res)++;
            DFS(g, g->vex[i]);
            //或者
            //BFS(g, g->vex[i]);
        }
    }

就是说上面这个循环我们只是找到一个起始位置开始遍历,虽然这个循环原则上要走g->vexnum次,但是如果它是连通图,当它从起点进入一次DFS或者BFS,后面就直接一次打印出全部结果了。当递推结束回溯返回时, ==if (!visited[g->vex[i]])==这个if永远为假,因为每一个字符都已经被遍历了,visited[g->vex[i]]都为1,加个“!”恒为假。所以不再DFS。res也就不再计数了。
但是如果它是非连通图,一次DFS遍历不完全部的字符,所以当递推结束回溯回到这个for循环时,会再次进入if表达式语句,这个时候计数器加一。并且每次调用这个DFS的顶点字符恰好是各个连通分量的顶点。结论:调用几次遍历过程,说明该图有几个连通分量。

void TraverseG(AdiMatrix *g, int *res) {
    for (int i = 1; i <= g->vexnum; i++) {
        if (!visited[g->vex[i]]) {
            (*res)++;
            DFS(g, g->vex[i]);
            //或者
            //BFS(g, g->vex[i]);
        }
    }
}
int main(int argc, const char * argv[]) {
    int res = 0;
    AdiMatrix *g;
    g = (AdiMatrix *)malloc(sizeof(AdiMatrix));
    Create(g);
    TraverseG2(g, &res);
    printf("%d\n", res);
    return 0;
}

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值