一.两种遍历
1.深度优先搜索(DFS)
深度优先搜索,顾名思义,就是优先向更深的地方搜索。假设这是一棵树,那么深度优先搜索会优先向深度
更高的结点搜索。因为深度优先搜索的概念,要到最深的结点后才返回,所以同层次的结点是先访问,后遍历,符合栈的结构模型,所以我们的深度优先搜索一般都是通过递归实现。
对于这个像树的图来讲:
假设我们的从1开始遍历,那么他会优先访问深度更高的顶点:
最终,它的遍历顺序为:
GraphG DFS:1 2 4 8 5 3 6 7
2.广度优先搜索(BFS)
定义与DFS相似,广度优先搜索是优先将同一层次的结点搜索完成后,再向下一个深度的结点搜索。又用树来举例子:假设对一棵树进行广度优先搜索,那么它会首先先将自己的兄弟结点遍历完后,才会向更深的儿子结点进行遍历。所以,同一结点的儿子们是先访问的先遍历,所以很符合队列的模型,所以我们一般通过队列实现。
还是对于这个图
我们广度优先搜索的过程为:
结果是:
GraphG BFS:1 2 3 4 5 6 7 8
3.两者的差别
用两张图直观的表示这两种搜索,比如A方块是在迷宫里,我们要遍历它到终点的路。
深度优先搜索:
广度优先搜索:
二.代码实现
看看效果:
因为图个各种构造,所以我们采用有向图+邻接表的方法来实现。
先构造顶点,再构造弧。
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
#define MaxLen 100
typedef struct ArcNode{
int Head;
struct ArcNode *nextArc;
}ArcNode; // 创建弧结点
typedef struct VNode{
char NodeName;
ArcNode *nextArc;
}VNode; // 创建Vertex顶点结点
typedef struct Graph{
int len;
VNode *Nbase; // 线性存储顶点结点
}Graph; // 创建一张图
int MarkGraph[MaxLen]; // 标记数组
Graph *GraphCreat(void);
int GraphShowList(Graph *G);
int GraphDFS(Graph *G);
int DFS(Graph *G, int vertex);
int GraphBFS(Graph *G);
int main()
{
Graph *map = GraphCreat();
GraphShowList(map);
GraphDFS(map);
GraphBFS(map);
return 0;
}
Graph *GraphCreat(void)
{
Graph *G = (Graph*)malloc(sizeof(Graph));
int lenV, lenA;
int i, tail, head;
char temp;
ArcNode *ArcNow, *ArcLast;
printf("please input the number of Vertex and Arc:");
scanf("%d %d", &lenV, &lenA); // 获取 顶点数和弧的个数
if (lenV > MaxLen)
{
printf("Over MaxLenth");
exit(ERROR);
} // 限制下大小
// 创建顶点线性表
G->len = lenV;
G->Nbase = (VNode*)malloc(sizeof(VNode)*lenV);
// 存顶点名字
printf("please input Vertexs' name:\n");
rewind(stdin);
for (i = 0; i < lenV; ++i)
{
scanf("%c", &temp);
(G->Nbase+i)->NodeName = temp;
(G->Nbase+i)->nextArc = NULL;
}
// 存弧
printf("please input (tail,head) as the arc:\n");
for (i = 0; i < lenA; ++i)
{
rewind(stdin);
scanf("%d %d", &tail, &head);
// 如果是顶点的第一个弧
if ((G->Nbase+tail)->nextArc == NULL)
{
(G->Nbase+tail)->nextArc = (ArcNode*)malloc(sizeof(ArcNode));
(G->Nbase+tail)->nextArc->Head = head;
(G->Nbase+tail)->nextArc->nextArc = NULL;
}
else // 非顶点的第一个弧
{
ArcNow = (G->Nbase+tail)->nextArc;
while(ArcNow->nextArc != NULL) // 循环移动到最后一个弧
{
ArcNow = ArcNow->nextArc;
}
// 创建弧结点
ArcNow->nextArc = (ArcNode*)malloc(sizeof(ArcNode));
ArcNow->nextArc->Head = head;
ArcNow->nextArc->nextArc = NULL;
}
}
return G;
}
int GraphShowList(Graph *G) // 循环输出每个顶点的弧信息
{
int i;
ArcNode *ArcNow;
printf("\n");
for (i = 0; i < G->len; ++i) // 循环
{
printf("%d: %c", i, (G->Nbase+i)->NodeName); // 输出顶点名
ArcNow = (G->Nbase+i)->nextArc;
while(ArcNow != NULL) // 循环输出每个弧
{
printf("->%c", (G->Nbase+ArcNow->Head)->NodeName);
ArcNow = ArcNow->nextArc;
}
printf("\n");
}
}
int GraphDFS(Graph *G) // DFS初始化
{
int i = 0;
for (i = 0; i < G->len; ++i) // 更新结点标记数组
{
MarkGraph[i] = 0;
}
printf("\nGraphG DFS:");
DFS(G, 0); // 开始DFS
}
int DFS(Graph *G, int vertex)
{
ArcNode *ArcNow;
if (MarkGraph[vertex] == 0) // 如果该顶点未被访问过
{
printf("%c ", (G->Nbase+vertex)->NodeName); // 输出该顶点
MarkGraph[vertex] = 1; // 标记该顶点被访问过
ArcNow = (G->Nbase+vertex)->nextArc; // 下一条弧
while(ArcNow != NULL) // 循环递归该顶点的所有弧
{
DFS(G, ArcNow->Head);
ArcNow = ArcNow->nextArc;
}
}
}
int GraphBFS(Graph *G) // BFS
{
int *Queue = (int*)malloc(sizeof(int) * G->len * G->len); // 创建一个模拟队列
int QFront, QTail, vertex, i; // 队列顶指针 队列尾
ArcNode *ArcNow;
Queue[0] = 0; // 把0号顶点压入队列
QFront = 0;
QTail = 1;
for (i = 0; i < G->len; ++i) // 初始化标记数组
{
MarkGraph[i] = 0;
}
printf("\nGraphG BFS:");
while(QFront < QTail) // 队尾队头相等,则队列空
{
vertex = Queue[QFront]; // 队头出队
QFront += 1;
if (MarkGraph[vertex] == 0) // 类似于DFS了
{
printf("%c ", (G->Nbase+vertex)->NodeName); // 输出当前顶点信息
MarkGraph[vertex] = 1; // 标记当前顶点已用
ArcNow = (G->Nbase+vertex)->nextArc; // 开始循环
while(ArcNow != NULL) // 非空
{
Queue[QTail] = ArcNow->Head; // 入队
QTail += 1;
ArcNow = ArcNow->nextArc; // 迭代
}
}
}
printf("\n");
}