DFS与BFS

一.DFS

DFS简称Depth First Search,深度优先搜索。即通过递归,不断向深处搜索。先遍历根结点,逐个遍历每个根节点的子节点,一直走到尽头后,再return原路返回,以此往复。

1.DFS遍历vector构图树

先用vector构出树形图(前文有详细讲解),接着我们进行深度优先搜索。

cpp 复制代码
void dfs(int x)//顺序从下到上从左往右
{
	cout << x << " ";
	st[x] = true;
	for (auto e : edges[x])
	{
		if (!st[e])
		{
			dfs(e);
		}
	}
}

我们使用一个bool类型的数组st,每当节点被遍历过之后,我们就将他设置为true;紧接着便利edges数组(存储着结点x的子节点数据),若该子节点未被遍历,那么就进行下一轮的深度递归。以此往复,我们就按照从上到下从左往右的方式遍历了树。

下面放上完整代码

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int n;
const int N = 1e5;
vector<int> edges[N];
bool st[N];

void dfs(int x)//顺序从下到上从左往右
{
	cout << x << " ";
	st[x] = true;
	for (auto e : edges[x])
	{
		if (!st[e])
		{
			dfs(e);
		}
	}
}

int main()
{
	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;
}

2.DFS遍历链式结构树

我们用链式结构构造出树(前文有讲解)进行深度搜索。

cpp 复制代码
void dfs(int x)//由于该链式结构采用的是头插,所以在打印的时候是优先从下到上从右往左打印的
{
	cout << x << " ";
	st[x] = true;
	for (int i = h[x]; i; i = ne[i])
	{
		int v = e[i];
		if (!st[v])
		{
			dfs(v);
		}
	}
}

先定义的bool类型数组st,每当结点被遍历过之后,就给该点设置为true;紧接着遍历该结点的哨兵位h,通过h我们可以找到该结点的子结点,通过子结点的ne指针域我们可以找到其他与之连接的子结点,从而达到遍历树。在遍历时需要判断该点是否为true(被遍历过)。

下面放上完整代码:

cpp 复制代码
#include <iostream>
using namespace std;

int n;
const int N = 1e5;
int e[2 * N], ne[2 * N], h[N], id;
bool st[N];

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

void dfs(int x)//由于该链式结构采用的是头插,所以在打印的时候是优先从下到上从右往左打印的
{
	cout << x << " ";
	st[x] = true;
	for (int i = h[x]; i; i = ne[i])
	{
		int v = e[i];
		if (!st[v])
		{
			dfs(v);
		}
	}
}

int main()
{
	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;
}

由于该结点的存储方式是头插的形式,所以在遍历的时候是按照左中右的形式输出的结果。

3.关于DFS的时间复杂度与空间复杂度

假设一棵树上有n个数,那么他就存在着n-1条边,我们从根节点向下遍历遍历至没有子节点时才返回,所以说每一条边都被遍历了2次,那么就是2*(n-1)次一共,那么时间复杂度就是O(n)。

假设最复杂的情况,这棵树的所有结点都连成一条线,空间复杂度最差的情况下为O(n)。

二.BFS

所谓BFS(Breadth First Search),俗称广度优先搜索。广度优先搜索即对树进行逐层的遍历。

这里我们使用一个队列来实现该方法对树的遍历。

让根结点进入队列,紧接着让该根结点的所有子结点进入队列,接着输出根结点;紧接着对下一个头结点进行操作,让该头结点的所有子结点进入队列,接着输出头结点进行打印,以此往复。

1.BFS遍历vector构图树

我们用vector结构构造树,用BFS遍历。

cpp 复制代码
void bfs()
{
	queue<int> q;
	q.push(1);
	st[1] = true;

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

		for (auto e : edges[u])
		{
			if (!st[e])//防止打印重复需要额外判断一步
			{
				q.push(e);//根结点出队列子节点进队列
				st[e] = true;
			}
		}
	}
}

同样的我们使用一个bool类型数组来判断结点是否遍历,接着就是对上述过程进行一个模拟实现,当队列中存在数的时候循环继续,第二个for循环是用来遍历该结点的子节点数据,该处需要用一个if语句来判断是否遍历,以及每当遍历完该点数据后都要设置为true。

下面是完整代码:

cpp 复制代码
#include <queue>
#include <iostream>
#include <vector>
using namespace std;

int n;
const int N = 1e5;
vector<int> edges[N];
bool st[N];

void bfs()
{
	queue<int> q;
	q.push(1);
	st[1] = true;

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

		for (auto e : edges[u])
		{
			if (!st[e])//防止打印重复需要额外判断一步
			{
				q.push(e);//根结点出队列子节点进队列
				st[e] = true;
			}
		}
	}
}

int main()
{
	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();
	return 0;
}

输出的结果中,我们可以得到一份中左右的层序遍历输出。

2.BFS遍历链式结构树

用链式结构构造出树。

cpp 复制代码
void bfs()
{
	queue<int> q;
	q.push(1);
	st[1] = true;
	while (q.size())
	{
		int u = q.front(); q.pop();
		cout << u << " ";
		st[u] = true;
		for (int i = h[u]; i; i = ne[i])
		{
			int v = e[i];
			if (!st[v])
			{
				q.push(v);
				st[v] = true;
			}
		}
	}
}

我们使用一个bool类型数组来判断结点是否遍历过,若被遍历过则为true。套入一个循环,若队列中还存在数就继续进行操作,根结点入队,打印根结点并且设置为true。接着通过根结点在h哨兵位存储的信息,找到他的子结点信息,若该子节点未被遍历过就入队列。如此循环往复。

下面是完整代码:

cpp 复制代码
#include <iostream>
#include <queue>
using namespace std;

int n;
const int N = 1e5;
int e[2 * N], ne[2 * N], h[N], id;
bool st[N];

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

void bfs()
{
	queue<int> q;
	q.push(1);
	st[1] = true;
	while (q.size())
	{
		int u = q.front(); q.pop();
		cout << u << " ";
		st[u] = true;
		for (int i = h[u]; i; i = ne[i])
		{
			int v = e[i];
			if (!st[v])
			{
				q.push(v);
				st[v] = true;
			}
		}
	}
}

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

3.关于BFS的时间复杂度与空间复杂度

假如有n个数,由于每个数只会进队列一次并且出队列一次,所以时间复杂度为O(n)

假如有n个数,在最坏的情况下,所以的子结点都在同一层,那么需要入队n-1个数,空间复杂度为O(n)

三.总结

无论是DFS还是BFS他们的时间复杂度空间复杂度都是O(n),对于这两种方法,我们都是在遍历树的基础上实现的。DFS运用的递归原型也可以理解为树型遍历,BFS使用队列的方式实现效率更高且更直观。

创作不易感谢大家支持,一起加油!

相关推荐
旖-旎1 小时前
《LeetCode 53 最大子数组和 || LeetCode 918 环形子数组的最大和》
c++·算法·leetcode·动态规划
变量未定义~1 小时前
单调栈+倍增思想 皇家守卫【算法赛】、单调队列 附近最小
算法
QN1幻化引擎1 小时前
给 AI 做一次「意识体检」——基于 QN1 幻化引擎的灵鉴意识识别框架与 DalinX V5 实测
大数据·数据结构·人工智能·算法·架构
拂拉氏1 小时前
【知识讲解】 AVL树从基本成员的介绍到核心接口的实现(插入、判断、删除等等)
数据结构·算法·avl树
可靠的仙人掌2 小时前
SAC(Soft Actor-Critic)算法底座
开发语言·算法·php
海石3 小时前
单调栈复健,顺便,牺牲一下吧,空间复杂度!一切献给AC
算法·leetcode
海石3 小时前
JS击败94%,Hard题想不到动态规划,那就用数组和栈试试
算法·leetcode
山峰哥4 小时前
数据库工程与SQL优化实战指南‌
数据库·sql·oracle·深度优先·宽度优先
学究天人5 小时前
数学公理体系大全:Comprehensive Collection of Mathematical Axiom Systems(卷7)
线性代数·矩阵·动态规划·概率论·图论·抽象代数·拓扑学
alphaTao6 小时前
LeetCode 每日一题 2026/7/6-2026/7/12
算法·leetcode