什么是DFS、BFS?
一条线走到底,深度优先遍历,每一个顶点只遍历、只打印一次的方式:DFS、BFS 数据结构 --- 图的存储
单纯地把邻接顶点的邻接顶点打印出来,顶点重复遍历,打印多次
从 A→F 所有的节点都遍历完了,需要做回退,从F回退到D点,找D点有没有邻接顶点,没有邻接顶点,继续做回退,退到B节点,有未访问的邻接顶点,访问C顶点
如果遍历的节点已经被遍历了,不能重复遍历,要做回退
如果形成环状,也是重复遍历的情况,也要做回退
要做回退,用栈实现
当前节点如果不存在邻接顶点,就要做出栈操作
先把 A 的所有邻接顶点 B、C 遍历,按照当前节点的邻接顶点的遍历顺序做下一次遍历,如果 B 先遍历,就遍历 B 的所有邻接顶点,由于 B 的邻接顶点 C 已经被遍历了所以不需要遍历
做遍历时要判断当前节点是否被遍历,如果被遍历了就不需要遍历
用队列实现,通过出队的方式决定下一次遍历的顺序
先遍历 A,遍历 A 后,遍历 A 的邻接顶点 B、C,把 B、C 入队,当遍历完 A 的所有邻接顶点后,B出队,访问B的所有邻接顶点D,D访问完后入队,B没有邻接顶点,出队C,访问C的邻接顶点,C的邻接顶点为E,入队E
队列为空,整个图就遍历完成了
DFS 实现
连接方式与边的插入方式有关
准备一个栈,栈中存储的是位置 LPNODE,要做回退
邻接链表中的节点实际存的是序号,把序号拿出来
①从 A 入口进来,遍历 A 的横向链表,把 C 节点遍历完,入栈
②跳到 C 数组的位置,从 C 数组的位置,横向遍历 E 节点(跳到以 C 为头的横向链表中 C → E ),遍历完 E 节点后,跳到 E 的位置,遍历它的邻接顶点,E 的邻接顶点是 C,C 已经被遍历了,要做回退,退到链表 E 的位置,访问下一个邻接顶点,下一个邻接顶点没有被访问就访问. . .
横向遍历的特点:从链表中找一个没有被访问的节点遍历,只要找到一个即可
//打印字符串-> 字符串数组
void printData(const char* str)
{
printf("%s\t", str);
}
//遍历的图 入口:用序号表示第几个元素
void DFSTraverse(LPGRAPH g,int inPos)
{
//准备一个栈
LPNODE stack[MAX];
int top = -1;
//访问标记->被访问置为1
int visited[MAX] = { 0 };
//1.访问入口
printData(g->vextex[inPos].data);//打印顶点信息-> 第几个顶点
visited[inPos] = 1; //对访问标记做处理-> 当前元素标记置为1表示已经被访问
//定义一个移动的指针做横向遍历
LPNODE pmove = g->vextex[inPos].firstNode;
while (top != -1 ||pmove != NULL)//栈不为空 pmove不为空
{
while (pmove != NULL)
{
//如果当前顶点的访问标记为1表示被访问,找下一个没有访问节点
if (visited[pmove->index] == 1)
{
pmove = pmove->next;
}
else
{
printData(g->vextex[pmove->index].data);//访问数据
visited[pmove->index] = 1;//访问后标记做处理
stack[++top] = pmove; //入栈
//跳到访问到的节点的那个横向链表中去
pmove = g->vextex[pmove->index].firstNode;
}
}
if (top != -1) //pmove==NUll就做出栈操作
{
pmove = stack[top--]; //出栈
pmove = pmove->next; //回退到B的位置而不是E的位置->退到E的位置的下一个位置因为E位置已经被访问了
}
}
}
BFS 实现
连接方式与边的插入方式有关
①从 A 入口进来,把整个 A 的横向链表遍历完,遍历到空的位置结束
②把序号(位置)做入队操作:要知道哪个顶点是先遍历的,作为下一次遍历的依据,只需要准备一个队列,存放序号即可,不需要存节点,例如:B 节点是序号 1,c 节点是序号 2,只需要把 1,2 入队即可
③把 A 的横向链表遍历完后,1 出队,直接走到 1 的位置,做横向遍历,把 B 的横向链表遍历完
④注意要防止重复遍历,重复遍历的不做遍历,只遍历 D,F 即可,访问的节点也需要有一个标记,把 B 的横向链表遍历完后也需要把序号3、5入队
⑤把 2 出队. . .
把整个横向链表遍历完,已经做遍历的,不做遍历
可以用循环队列,普通队列如果节点过多,会出现伪溢出
//遍历的图 入口
void BFSTraverse(LPGRAPH g,int inPos)
{
//访问标记
int visited[MAX] = { 0 };
int queue[MAX];//准备一个队列
int front = -1;//队头
int rear = -1; //队尾
//访问入口-> 打印入口的值
printData(g->vextex[inPos].data);
//对访问标记做处理-> 1表示被访问
visited[inPos] = 1;
//入队-> 队尾做变化 从-1开始前置++
queue[++rear] = inPos;
//定义一个移动的指针作为横向遍历
LPNODE pmove = NULL;
//队头小于队尾
while (front < rear)
{
inPos = queue[++front]; //出队
pmove = g->vextex[inPos].firstNode; //遍历当前序号下的横向链表 不为空打印里面的值
while (pmove != NULL)
{
if (visited[pmove->index] == 0) //遍历前判断是否被访问,如果被访问就没必要访问
{
printData(g->vextex[pmove->index].data);
visited[pmove->index] = 1; //访问后把访问标记做处理
queue[++rear] = pmove->index; //把遍历节点的序号入队
}
pmove = pmove->next; //被访问走到下一个节点
}
}
}
测试代码
int main()
{
LPGRAPH g = createGraph();
printf("DFS:\n");
DFSTraverse(g,0);//从第0个节点开始遍历-> A节点
printf("\nBFS:\n");
BFSTraverse(g, 0);
return 0;
}
/*输出*/
请输入边和顶点数:10 7
请输入所有顶点:A B C D E F G
请输入边的信息:
A B
A C
B C
B D
B F
D F
C E
F E
C G
E G
DFS:
A C G E F D B
BFS:
A C B G E F D