【算法通关指南:数据结构与算法篇】树形结构遍历指南:DFS 递归深搜与 BFS 队列广搜实战解析

🔥小龙报:个人主页

🎬作者简介:C++研发,嵌入式,机器人方向学习者

❄️个人专栏:《算法通关指南》
永远相信美好的事情即将发生

文章目录

  • 前言
  • 一、深度优先遍历-DFS
    • [1.1 vector数组](#1.1 vector数组)
    • [1.2 链式前向星](#1.2 链式前向星)
    • [1.3 复杂度分析](#1.3 复杂度分析)
      • [1.3.1 时间复杂度](#1.3.1 时间复杂度)
      • [1.3.2 空间复杂度](#1.3.2 空间复杂度)
  • 二、宽度优先遍历-BFS
    • [2.1 vector数组](#2.1 vector数组)
    • [2.2 链式前向星](#2.2 链式前向星)
    • [2.3 复杂度分析](#2.3 复杂度分析)
      • [2.3.1 时间复杂度](#2.3.1 时间复杂度)
      • [2.3.2 空间复杂度](#2.3.2 空间复杂度)
  • 总结与每日励志

前言

树的遍历就是不重不漏的将树中所有的点都扫描⼀遍

在之前学过的线性结构中,遍历就很容易,直接从前往后扫描⼀遍即可。但是在树形结构中,如果不按照⼀定的规则遍历,就会漏掉或者重复遍历⼀些结点。因此,在树形结构中,要按照⼀定规则去遍历。常用的遍历方式有两种,一种是深度优先遍历(DFS),另⼀种是宽度优先遍历(BFS)

我们将以一道例题讲解这些遍历方法:

树的逻辑图

一、深度优先遍历-DFS

深度优先遍历,英文缩写为DFS,全称是DepthFirstSearch,中文名是深度优先搜索,是一种用于遍历或搜索树或图的算法。所谓深度优先,就是说每次都尝试向更深的节点走,也就是⼀条路走到黑

具体流程:

(1) 从根节点出发,依次遍历每⼀棵子树;

(2) 遍历子树的时候,重复第一步。

因此,深度优先遍历是⼀种递归形式的遍历可以用递归来实现

注:存储树结构的时候,会把相邻的所有结点都存下来,这样在扫描子树的时候会直接扫描到上一层,这不是我们想要的结果。

因此,需要一个数组来标记,哪些结点已经访问过,接下来的时候,就不去遍历那些点。

1.1 vector数组

c 复制代码
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
vector<int> edges[N];   // edges[i] 保存着i号结点相连的所有点
bool st[N];  // 标记当前结点是否已经被遍历过

// 当前遍历到u这棵⼦树
void dfs(int u)
{
	// 先访问该点
	cout << u << " ";
	st[u] = true;  //标记表明该节点已被访问

	//访问子树
	for (auto v : edges[u])
	{
		if (!st[v])
			dfs(v);  //如果没有遍历过再去递归
	}
}

int main()
{
	int n;
	cin >> n;

	for (int i = 1; i < n; i++)
	{
		int a, b;
		cin >> a >> b;

		edges[a].push_back(b);
		edges[b].push_back(a);
	}

	dfs(1);
	return 0;
}

运行结果:

1.2 链式前向星

c 复制代码
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int h[N],ne[N * 2], e[N * 2],id;
bool st[N];

void add(int a, int b)
{
	id++;
	e[id] = b;
	ne[id] = h[a];
	h[a] = id;
}

void dfs(int u)
{
	cout << u << " ";
	st[u] = true;

	for (int i = h[u]; i; i = ne[i])
	{
		int v = e[i];
		if (!st[v])
			dfs(v);
	}
}
int main()
{
	int n;
	cin >> n;

	for (int i = 1; i < n; i++)
	{
		int a, b;
		cin >> a >> b;

		add(a, b);
		add(b, a);
	}
	dfs(1);
	return 0;
}

运行结果:

1.3 复杂度分析

1.3.1 时间复杂度

简单估计一下,在 遍历的整个过程中,会把树中所有的边扫描量两遍。边的数量为n - 1 故时间复杂度为O(N)。

1.3.2 空间复杂度

最差情况下,结点个数为n的树,高度也是n ,也就是变成一个链表。此时递归的深度也是n,故一个时间复杂度O(N);

二、宽度优先遍历-BFS

宽度优先遍历,英文缩写为BFS,全称是BreadthFirstSearch,也叫广度优先遍历。也是⼀种用于遍历或搜索树或图的算法。
所谓宽度优先。就是每次都尝试访问同⼀层的节点。如果同⼀层都访问完 了,再访问下⼀层。

算法过程可以看做是在树和图中,在起点放上一个细菌,每秒向周围的那些干净的位置扩散⼀层,直到把所有位置都感染
具体实现方式借助队列

(1)初始化一个队列;

(2)根节点入队,同时标记该节点已经入队;

(3)当队列不为空时,拿出队头元素,访问,然后将队头元素的所有孩子入队,同时打上标记。

(4)重复过程,直到队列为空。

2.1 vector数组

c 复制代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
vector<int> edges[N];
queue < int> q;
bool st[N];

void bfs(int u)
{
	q.push(u);
	st[u] = true;

	while (q.size())
	{
		auto u = q.front();
		cout << u << " ";
		q.pop();

		for (auto v : edges[u])
		{
			if (!st[v])
			{
				q.push(v);
				st[v] = true;
			}
		}
	}
}

int main()
{
	int n;
	cin >> n;

	for (int i = 1; i < n; i++)
	{
		int a, b;
		cin >> a >> b;

		edges[a].push_back(b);
		edges[b].push_back(a);
	}
	bfs(1);
	return 0;
}

运行结果:

2.2 链式前向星

c 复制代码
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e5 + 10;
queue<int> q;
int id, h[N], e[N * 2], ne[N * 2];
bool st[N];  //st[i]表示那些节点已经入队

void add(int a, int b)
{
	id++;
	e[id] = b;
	ne[id] = h[a];
	h[a] = id;
}

void bfs(int u)
{
	q.push(u);
	st[u] = true;

	while (q.size())
	{
		auto v = q.front();
		cout << v << " ";
		q.pop();

		// 让孩子入队
		for (int i = h[v]; i; i = ne[i])
		{
			int x = e[i];
			if (!st[x])
			{
				q.push(x);
				st[x] = true;
			}
		}
	}
}
int main()
{
	int n;
	cin >> n;

	for (int i = 1; i < n; i++)
	{
		int a, b;
		cin >> a >> b;
		add(a, b);
		add(b, a);
	}

	bfs(1);
	return 0;
}

运行结果:

2.3 复杂度分析

2.3.1 时间复杂度

有结点只会入队一次,然后出队一次,因此时间复杂度为空间复杂度:O(N)

2.3.2 空间复杂度

最坏情况下,所有的非根结点都在同⼀层,此时队列里面最多有n - 1个元素,故空间复杂度为O(N);

总结与每日励志

本文介绍了树遍历的两种主要方法:深度优先遍历(DFS)和宽度优先遍历(BFS)。DFS采用递归方式,每次尝试向更深节点遍历;BFS借助队列实现,按层次遍历节点。两种方法的时间复杂度均为O(N),空间复杂度在最坏情况下也是O(N)。文章通过示例代码展示了两种遍历的具体实现方式,包括vector数组和链式前向星两种存储结构。最后以"永远相信美好的事情即将发生"作为励志结语。

相关推荐
代码游侠24 分钟前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
想进个大厂28 分钟前
代码随想录day37动态规划part05
算法
sali-tec29 分钟前
C# 基于OpenCv的视觉工作流-章22-Harris角点
图像处理·人工智能·opencv·算法·计算机视觉
子春一39 分钟前
Flutter for OpenHarmony:构建一个 Flutter 四色猜谜游戏,深入解析密码逻辑、反馈算法与经典益智游戏重构
算法·flutter·游戏
MZ_ZXD0011 小时前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php
人道领域1 小时前
AI抢人大战:谁在收割你的红包
大数据·人工智能·算法
TracyCoder1231 小时前
LeetCode Hot100(34/100)——98. 验证二叉搜索树
算法·leetcode
A尘埃1 小时前
电信运营商用户分群与精准运营(K-Means聚类)
算法·kmeans·聚类
2的n次方_2 小时前
Runtime 执行提交机制:NPU 硬件队列的管理与任务原子化下发
c语言·开发语言
A星空1232 小时前
一、Linux嵌入式的I2C驱动开发
linux·c++·驱动开发·i2c