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

什么是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
  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
的深度优先搜索(DFS)和广度优先搜索(BFS)是两种常见的遍历算法。 假设我们有一个无向,其中边用边列表表示如下: ``` edges = [(0, 1), (0, 2), (1, 2), (2, 0), (2, 3), (3, 3)] ``` 其中每个元素表示一条边,例如 `(0, 1)` 表示节点 `0` 和节点 `1` 之间有一条边。 ### DFS DFS 通过递归地访问每个节点及其邻居来遍历。具体实现如下: ```python # 的深度优先搜索 def dfs(graph, start): # visited 用于记录已经访问的节点 visited = set() # 进行递归访问 def traverse(node): visited.add(node) print(node, end=' ') for neighbor in graph[node]: if neighbor not in visited: traverse(neighbor) traverse(start) ``` 在上面的代码中,我们首先定义了一个 `visited` 集合,用于记录已经访问的节点。然后我们定义了一个内部函数 `traverse(node)`,它会递归访问每个节点及其邻居。在递归访问每个节点时,我们首先将其添加到 `visited` 集合中,然后打印出节点的值,并递归访问其未被访问过的邻居。 我们可以使用以下代码调用 `dfs` 函数,以从节点 `2` 开始遍历: ```python # 测试 DFS graph = {0: [1, 2], 1: [2], 2: [0, 3], 3: [3]} dfs(graph, 2) ``` 输出如下: ``` 2 0 1 3 ``` ### BFS BFS 通过先访问起点节点,然后逐层访问其邻居节点来遍历。具体实现如下: ```python # 的广度优先搜索 def bfs(graph, start): # visited 用于记录已经访问的节点 visited = set() # 使用队列进行遍历 queue = [start] visited.add(start) while queue: node = queue.pop(0) print(node, end=' ') for neighbor in graph[node]: if neighbor not in visited: visited.add(neighbor) queue.append(neighbor) ``` 在上面的代码中,我们首先定义了一个 `visited` 集合和一个队列 `queue`,用于记录已经访问的节点和待访问的节点。然后我们将起点节点添加到队列中,并将其标记为已访问。在接下来的循环中,我们不断从队列中取出一个节点,并打印出其值,然后逐个访问其未被访问过的邻居节点,并将其添加到队列中,同时将其标记为已访问。 我们可以使用以下代码调用 `bfs` 函数,以从节点 `2` 开始遍历: ```python # 测试 BFS graph = {0: [1, 2], 1: [2], 2: [0, 3], 3: [3]} bfs(graph, 2) ``` 输出如下: ``` 2 0 3 1 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qiuqiuyaq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值