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

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

相关推荐
KeyPan4 分钟前
【机器学习:八、逻辑回归】
人工智能·算法·机器学习·数据挖掘·逻辑回归
yonuyeung10 分钟前
代码随想录算法【Day16】
数据结构·算法·leetcode
YiHanXii24 分钟前
大模型算法工程师相关面试
算法·面试·职场和发展
_周游1 小时前
【C语言】_冒泡排序及其优化思路
c语言·算法·排序算法
拓端研究室TRL1 小时前
Copula算法原理和R语言股市收益率相依性可视化分析
开发语言·算法·r语言·概率论
唔皇万睡万万睡1 小时前
基于DFT与IIR-FIR滤波器的音频分析与噪声处理
算法·matlab·音频·信号处理
无限码力1 小时前
华为OD算法真题目录
算法·职场和发展·求职招聘·笔试·机试
迪小莫学AI2 小时前
# 解题分享:LeetCode 2001. 可互换矩形的组数
算法·leetcode·职场和发展
一叶祇秋2 小时前
Leetcode - 周赛430
算法·leetcode·职场和发展