图论核心:深度搜索DFS 与广度搜索BFS

一、深度优先搜索(DFS):一条路走到黑的探索哲学

1. 算法核心思想

DFS(Depth-First Search)遵循 "深度优先" 原则,从起始节点出发,尽可能深入地访问每个分支,直到无法继续时回溯,尝试其他路径。这种策略类似走迷宫时 "不撞南墙不回头" 的探索方式,通过递归或栈结构实现路径的遍历与回溯。

2. 经典场景:全排列问题(字典序输出)
问题描述 :给定整数 n,按字典序输出 1~n 的所有排列。
算法分析

  • ans数组记录当前排列,mark数组标记数字是否已使用。
  • 递归函数dfs(u)表示填充第 u 位数字,通过枚举 1~n 的未使用数字,递归构建全排列。
  • 回溯时需清除标记,确保数字可重复选择(不同排列路径)。

C++ 代码实现

cpp

复制代码
#include <iostream>
using namespace std;
const int N = 10;
int ans[N], n;
bool mark[N]; // 标记数字是否已使用

void dfs(int u) {
    if (u == n) { // 递归终止条件:填满所有位置
        for (int i = 0; i < n; i++) cout << ans[i];
        cout << endl;
        return;
    }
    for (int i = 1; i <= n; i++) {
        if (!mark[i]) { // 选择未使用的数字
            mark[i] = true;
            ans[u] = i;
            dfs(u + 1); // 递归填充下一位
            mark[i] = false; // 回溯:清除标记
            ans[u] = 0; // 重置当前位
        }
    }
}

int main() {
    cin >> n;
    dfs(0);
    return 0;
}

3. 算法特性与应用

  • 空间效率:递归深度为图的高度,最坏情况下需 O (n) 空间(如树退化为链表)。
  • 典型应用
    • 拓扑排序(有向无环图 DAG)
    • 连通分量检测(无向图)
    • 路径搜索与环检测
二、广度优先搜索(BFS):层次扩展的最短路径探索

1. 算法核心思想

BFS(Breadth-First Search)以 "层" 为单位遍历图,从起始节点出发,先访问所有相邻节点(第一层),再依次访问下一层节点。这种策略确保首次访问某节点时路径最短,因此天然适用于最短路径问题。

2. 经典场景:迷宫最短路径
问题描述 :在 n×m 的迷宫中,从左上角 (0,0) 到右下角 (n-1,m-1),求最少移动次数(0 可走,1 为墙)。
算法分析

  • 使用队列实现层次遍历,mark数组记录到达各点的步数(初始为 - 1 表示未访问)。
  • 方向向量dx/dy定义上下左右移动,每次从队列取出当前节点,向四个方向扩展新节点。
  • 首次到达终点时的步数即为最短路径(BFS 的层次特性保证)。

C++ 代码实现

cpp

复制代码
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int, int> PII; // 坐标对
const int N = 110;
int map[N][N], mark[N][N]; // map存储迷宫,mark存储步数
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; // 上下左右方向
int n, m;

void bfs() {
    memset(mark, -1, sizeof mark); // 初始化步数为-1
    queue<PII> q;
    q.push({0, 0}); // 起点入队
    mark[0][0] = 0; // 起点步数为0

    while (!q.empty()) {
        PII top = q.front(); // 取出当前节点
        q.pop();

        for (int i = 0; i < 4; i++) { // 遍历四个方向
            int nx = top.first + dx[i], ny = top.second + dy[i];
            // 检查边界、是否可通行、是否未访问
            if (nx >= 0 && nx < n && ny >= 0 && ny < m && 
                map[nx][ny] == 0 && mark[nx][ny] == -1) {
                mark[nx][ny] = mark[top.first][top.second] + 1; // 步数+1
                q.push({nx, ny}); // 新节点入队
                if (nx == n - 1 && ny == m - 1) return; // 提前终止(找到终点)
            }
        }
    }
}

int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            cin >> map[i][j];
    bfs();
    cout << mark[n-1][m-1] << endl; // 输出最短步数
    return 0;
}

3. 关键细节解析

  • 队列的作用:保证按层遍历,先入队的节点先处理,确保首次访问即最短路径。
  • 方向向量:通过数组统一管理移动方向,避免重复编写坐标变换代码。
  • 标记数组的双重功能:既记录是否访问过,又存储最短步数,空间效率更高。
三、DFS 与 BFS 对比与选型建议
特性 DFS BFS
遍历方式 深度优先,递归 / 栈实现 广度优先,队列实现
空间复杂度 O (深度)(可能栈溢出) O (宽度)(适合大规模图需优化)
最短路径 不适用(非层次遍历) 天然支持(首次访问即最短路径)
典型场景 全排列、拓扑排序、环检测 最短路径、社交网络最近邻搜索

选型建议

  • 求路径存在性或枚举所有可能:优先选 DFS(代码简洁,递归实现更直观)。
  • 求最短路径或层次相关问题:必须选 BFS(利用队列的层次特性)。
四、扩展与优化技巧

1. DFS 优化

  • 迭代实现:用栈模拟递归,避免系统栈溢出(适用于 n>1e4 的场景)。
  • 剪枝策略:提前排除不可能的路径(如全排列中数字已使用),减少递归次数。

2. BFS 优化

  • 双向 BFS:从起点和终点同时搜索,相遇时即为最短路径,时间复杂度显著降低。
  • 优先队列(Dijkstra 算法):处理带权图的最短路径问题,本质是 BFS 的贪心扩展。
五、总结

DFS 与 BFS 是图论的基石算法,分别代表 "深度探索" 与 "层次扩展" 的思维模式。掌握两者的核心原理、代码实现及适用场景,是解决图论问题的关键。实际应用中,可根据问题特性灵活选择算法,并结合优化技巧提升效率。

相关推荐
2501_924747111 小时前
驾驶场景玩手机识别准确率↑32%:陌讯动态特征融合算法实战解析
人工智能·算法·计算机视觉·智能手机
limitless_peter2 小时前
优先队列,链表优化
c++·算法·链表
屁股割了还要学4 小时前
【数据结构入门】栈和队列
c语言·开发语言·数据结构·学习·算法·青少年编程
Monkey的自我迭代4 小时前
支持向量机(SVM)算法依赖的数学知识详解
算法·机器学习·支持向量机
阿彬爱学习5 小时前
AI 大模型企业级应用落地挑战与解决方案
人工智能·算法·微信·chatgpt·开源
L.fountain5 小时前
配送算法10 Batching and Matching for Food Delivery in Dynamic Road Networks
算法·配送
啊阿狸不会拉杆8 小时前
《算法导论》第 13 章 - 红黑树
数据结构·c++·算法·排序算法
qiuyunoqy8 小时前
蓝桥杯算法之搜索章 - 3
c++·算法·蓝桥杯·深度优先·dfs·剪枝
lifallen9 小时前
Kafka ISR机制和Raft区别:副本数优化的秘密
java·大数据·数据库·分布式·算法·kafka·apache
m0_6265352010 小时前
贪心算法学习 3 买卖股票的最佳时机 i ii
学习·算法·贪心算法