深度优先搜索(Depth-First Search,简称DFS)是一种用于遍历或搜索树或图的算法。它从一个节点开始,沿着尽可能深的分支进行搜索,直到达到目标或者不能再深入为止,然后回溯到上一个节点,继续搜索其他分支。
DFS通常用于以下几种情况:
1. 检查图是否连通或检查一个节点是否可达。
2. 寻找一条从起点到终点的路径。
3. 解决八皇后问题、图的遍历、拓扑排序等。
DFS可以递归地实现,也可以使用栈来实现非递归形式。
DFS的递归实现
以下是使用C++实现DFS的示例代码:
#include <iostream>
#include <vector>
using namespace std;
// 假设有一个图的邻接表表示
vector<int> graph[100];
// 用于标记节点是否被访问过
vector<bool> visited(100, false);
// DFS函数
void dfs(int node) {
// 访问当前节点
visited[node] = true;
cout << node << " ";
// 遍历所有邻接节点
for (int i = 0; i < graph[node].size(); i++) {
int adjNode = graph[node][i];
// 如果邻接节点未被访问,则递归访问它
if (!visited[adjNode]) {
dfs(adjNode);
}
}
}
int main() {
// 构建图的邻接表
graph[0].push_back(1);
graph[1].push_back(0);
graph[1].push_back(2);
graph[2].push_back(1);
graph[2].push_back(3);
graph[3].push_back(3);
// 从节点0开始DFS
dfs(0);
return 0;
}
这段代码首先定义了一个邻接表来表示图,然后使用一个布尔向量visited来跟踪每个节点是否已经被访问过。dfs函数递归地访问当前节点的所有未访问的邻接节点。
DFS的非递归实现
非递归的DFS使用栈来模拟递归过程:
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
// 假设有一个图的邻接表表示
vector<int> graph[100];
vector<bool> visited(100, false);
void dfsIterative(int start) {
stack<int> stack;
stack.push(start);
while (!stack.empty()) {
int node = stack.top();
stack.pop();
if (!visited[node]) {
visited[node] = true;
cout << node << " ";
// 将邻接节点压入栈中
for (int i = graph[node].size() - 1; i >= 0; i--) {
int adjNode = graph[node][i];
if (!visited[adjNode]) {
stack.push(adjNode);
}
}
}
}
}
int main() {
// 构建图的邻接表,与上面的示例相同
// 从节点0开始迭代DFS
dfsIterative(0);
return 0;
}
在这个非递归版本中,我们使用了一个栈来存储待访问的节点。我们首先将起始节点压入栈中,然后在循环中不断从栈中弹出节点进行访问,并将未访问的邻接节点压入栈中。
这两种DFS实现都可以有效地遍历图或树结构,选择哪一种取决于个人偏好和特定问题的需求。
广度优先搜索(Breadth-First Search,简称BFS)是一种遍历或搜索树和图的算法,它从一个节点开始,逐层遍历节点,即先访问所有邻接节点,再访问邻接点的邻接点,直到找到目标节点或遍历完所有节点。
BFS常用于以下场景:
1. 查找最短路径。
2. 查找所有连接节点的路径。
3. 解决社交网络中的朋友推荐问题。
BFS通常使用队列来实现。
BFS的实现
以下是使用C++实现BFS的示例代码:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// 假设有一个图的邻接表表示
vector<int> graph[100];
vector<bool> visited(100, false); // 标记节点是否被访问过
void bfs(int start) {
queue<int> q;
q.push(start); // 将起始节点入队
visited[start] = true; // 标记起始节点为已访问
while (!q.empty()) {
int node = q.front(); // 队头元素即为当前节点
q.pop();
cout << node << " "; // 访问当前节点
// 遍历当前节点的所有邻接节点
for (int i = 0; i < graph[node].size(); i++) {
int adjNode = graph[node][i];
if (!visited[adjNode]) {
q.push(adjNode); // 将未访问的邻接节点入队
visited[adjNode] = true; // 标记为已访问
}
}
}
}
int main() {
// 构建图的邻接表
graph[0].push_back(1);
graph[1].push_back(0);
graph[1].push_back(2);
graph[2].push_back(1);
graph[2].push_back(3);
graph[3].push_back(2);
// 从节点0开始BFS
bfs(0);
return 0;
}
这段代码首先定义了一个邻接表来表示图,然后使用一个布尔向量visited来跟踪每个节点是否已经被访问过。bfs函数使用一个队列来按层次遍历节点。
BFS的关键在于使用队列来保持访问顺序,确保先访问的节点先出队,这样可以保证搜索的广度优先性。在实际应用中,BFS可以有效地找到从起点到目标点的最短路径,因为它会先探索所有距离起点一样远的节点。
BFS算法的时间复杂度通常是O(V+E),其中V是顶点数,E是边数。空间复杂度取决于队列的大小,最坏情况下也是O(V)。