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使用队列的方式实现效率更高且更直观。

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

相关推荐
小O的算法实验室27 分钟前
IEEE TASE,基于MPC的多无人机协同搜索竞争群体优化方法
算法
Tbisnic1 小时前
BGE-M3 算法详解:从模型架构到三种检索方式的数学原理
算法·自然语言处理·大模型·bert·transformer·注意力机制
Brilliantwxx2 小时前
【算法从零到千】【55-58】哈希位图+常见数学运算 接口
算法
空堂与归3 小时前
用户分群找不到规律?用K-Means聚类算法自动发现数据模式
算法·机器学习·kmeans·聚类
动词ing3 小时前
【C语言】自定义函数+指针入门
c语言·开发语言·算法
重生之后端学习3 小时前
283. 移动零[简单]✅
开发语言·数据结构·算法·leetcode·职场和发展
一只小小的芙厨4 小时前
基础数论总结
笔记·学习·算法
夜不会漫长5 小时前
C++入门(1)
开发语言·c++·算法
wen_zhufeng6 小时前
IndexTTS 2.5 技术报告
人工智能·算法·机器学习
大熊背7 小时前
树莓派相机自动白平衡详解(四)
算法·树莓派·白平衡·isppipeline