BFS
概述
- 广度优先搜索(
Breadth-First Search
,简称BFS
)是一种遍历或搜索图或树数据结构的算法 - 它从根节点开始,沿着树的宽度遍历节点(即先访问同一层级的所有节点,再访问下一层级的节点)
- 在图中,
BFS
从起始节点开始,探索所有邻居,然后依次遍历每个邻居的邻居,直到访问到目标节点或所有节点
特征
- 层级遍历
BFS
按照层级顺序访问节点,即先访问距离起点最近的节点,再访问次近的节点
- 最短路径
- 在未加权图中,
BFS
可以找到从起点到目标节点的最短路径
- 在未加权图中,
步骤
- 初始化
- 创建一个队列,用于存储待访问的节点
- 将起始节点标记为已访问,并将其入队
- 迭代
- 从队列中取出一个节点,将该节点的所有未访问的邻居节点标记为已访问,并将其入队
- 重复上述过程,直到队列为空或找到目标节点
示例无向图
1 2 3 4 5 |
A / \ B C / \ \ D E F |
- 从节点
A
开始进行BFS
遍历 - 访问
A
,将A
的邻居B
和C
入队:Q = [B, C]
- 访问
B
,将B
的邻居D
和E
入队:Q = [C, D, E]
- 访问
C
,将C
的邻居F
入队:Q = [D, E, F]
- 访问
D
:Q = [E, F]
- 访问
E
:Q = [F]
- 访问
F
:Q = []
- 访问顺序为:
A -> B -> C -> D -> E -> F
时间复杂度
O(V + E)
,其中V
是节点数量,E
是边数量。每个节点和每条边都会被访问一次
空间复杂度
O(V)
,用于存储访问标记和队列
场景
- 最短路径问题
- 在无权图中找到从起点到目标节点的最短路径
- 连通性问题
- 检测图中所有节点是否连通,找到所有连通分量
- 树的层次遍历
- 逐层遍历树的节点
- 拓扑排序
- 在有向无环图中找到拓扑排序的一种方法
- 迷宫求解
- 在迷宫或网格中找到从起点到终点的最短路径
示例代码
1 2 3 4 5 |
0 / \ 1 2 / \ \ 3 4 5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include <iostream> #include <vector> #include <queue> using namespace std; void BFS(int start_node, const vector<vector<int>>& graph) { vector<bool> visited(graph.size(), false); queue<int> Q; visited[start_node] = true; Q.push(start_node); while (!Q.empty()) { int node = Q.front(); Q.pop(); cout << node << " "; for (int neighbor : graph[node]) { if (!visited[neighbor]) { visited[neighbor] = true; Q.push(neighbor); } } } } int main() { vector<vector<int>> graph = { {1, 2}, // 0 {0, 3, 4}, // 1 {0, 5}, // 2 {1}, // 3 {1}, // 4 {2} // 5 }; cout << "BFS traversal starting from node 0: "; BFS(0, graph); return 0; } |
DFS
概述
- 深度优先搜索(
Depth-First Search
,简称DFS
)是一种遍历或搜索图或树数据结构的算法 - 它沿着树的深度遍历节点,尽可能深入树的分支
- 在图中,
DFS
从起始节点开始,沿着一个分支尽可能深入节点,直到到达叶子节点或没有未访问的邻居节点- 然后回溯并探索其他分支
特征
- 深度优先
DFS
按照深度优先的策略访问节点,即尽可能深入每一个分支
- 递归或栈实现
DFS
可以使用递归或显式栈来实现
步骤
- 初始化
- 创建一个栈(或使用递归调用栈)用于存储待访问的节点
- 将起始节点标记为已访问,并将其入栈
- 迭代
- 从栈中取出一个节点,将该节点的所有未访问的邻居节点标记为已访问,并将其入栈
- 重复上述过程,直到栈为空或找到目标节点
示例无向图
1 2 3 4 5 |
A / \ B C / \ \ D E F |
- 初始化栈:
S = [A]
- 访问
A
,将A
的邻居B
和C
入栈:S = [C, B]
- 访问
B
,将B
的邻居D
和E
入栈:S = [C, E, D]
- 访问
D
:S = [C, E]
- 访问
E
:S = [C]
- 访问
C
,将C
的邻居F
入栈:S = [F]
- 访问
F
:S = []
- 访问顺序为:
A -> B -> D -> E -> C -> F
时间复杂度
O(V + E)
,其中V
是节点数量,E
是边数量。每个节点和每条边都会被访问一次
空间复杂度
O(V)
,用于存储访问标记和递归栈或显式栈
场景
- 路径查找
- 在图中找到从起点到目标节点的路径
- 连通性问题
- 检测图中所有节点是否连通,找到所有连通分量
- 拓扑排序
- 在有向无环图中找到拓扑排序的一种方法
- 图的遍历
- 遍历图的所有节点,执行特定操作
- 求解迷宫
- 在迷宫或网格中找到从起点到终点的路径
- 检测环
- 检测图中是否存在环
示例代码
- 栈实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include <iostream> #include <vector> #include <stack> using namespace std; void DFS(int start_node, const vector<vector<int>>& graph) { vector<bool> visited(graph.size(), false); stack<int> S; visited[start_node] = true; S.push(start_node); while (!S.empty()) { int node = S.top(); S.pop(); cout << node << " "; for (int neighbor : graph[node]) { if (!visited[neighbor]) { visited[neighbor] = true; S.push(neighbor); } } } } int main() { vector<vector<int>> graph = { {1, 2}, // 0 {0, 3, 4}, // 1 {0, 5}, // 2 {1}, // 3 {1}, // 4 {2} // 5 }; cout << "DFS traversal starting from node 0: "; DFS(0, graph); return 0; } |
- 递归实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <iostream> #include <vector> using namespace std; void DFSUtil(int node, const vector<vector<int>>& graph, vector<bool>& visited) { visited[node] = true; cout << node << " "; for (int neighbor : graph[node]) { if (!visited[neighbor]) { DFSUtil(neighbor, graph, visited); } } } void DFS(int start_node, const vector<vector<int>>& graph) { vector<bool> visited(graph.size(), false); DFSUtil(start_node, graph, visited); } int main() { vector<vector<int>> graph = { {1, 2}, // 0 {0, 3, 4}, // 1 {0, 5}, // 2 {1}, // 3 {1}, // 4 {2} // 5 }; cout << "DFS traversal starting from node 0: "; DFS(0, graph); return 0; } |
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 匹配_KMP模式匹配算法:二10/09
- ♥ 动态规划相关06/29
- ♥ 查找_顺序查找05/09
- ♥ Skia总结概述11/15
- ♥ 数据结构_最小优先级队列10/10
- ♥ 排序_基数排序09/04