文章目录
有向图的创建、求度、遍历
题目描述
从键盘接收有向图的顶点集,弧集,创建有向图,并完成下列任务:
(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;
}