数据结构 --- 图的遍历 DFS、BFS

什么是DFS、BFS?

数据结构 --- 图的遍历 DFS、BFS_第1张图片

一条线走到底,深度优先遍历,每一个顶点只遍历、只打印一次的方式:DFS、BFS 数据结构 --- 图的存储  

单纯地把邻接顶点的邻接顶点打印出来,顶点重复遍历,打印多次

从 A→F 所有的节点都遍历完了,需要做回退,从F回退到D点,找D点有没有邻接顶点,没有邻接顶点,继续做回退,退到B节点,有未访问的邻接顶点,访问C顶点

如果遍历的节点已经被遍历了,不能重复遍历,要做回退

如果形成环状,也是重复遍历的情况,也要做回退

要做回退,用栈实现

当前节点如果不存在邻接顶点,就要做出栈操作

数据结构 --- 图的遍历 DFS、BFS_第2张图片

先把 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 实现

数据结构 --- 图的遍历 DFS、BFS_第3张图片

连接方式与边的插入方式有关

准备一个栈,栈中存储的是位置 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 出队. . .

把整个横向链表遍历完,已经做遍历的,不做遍历

可以用循环队列,普通队列如果节点过多,会出现伪溢出

数据结构 --- 图的遍历 DFS、BFS_第4张图片

//遍历的图 入口
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

你可能感兴趣的:(数据结构,BFS,DFS)