【每日算法】Day 2-1:深搜与广搜终极指南——算法核心与场景剖析(C++实现)

今日详解深度优先搜索(DFS)与广度优先搜索(BFS),从遍历逻辑到场景应用,结合大厂真题与动图演示,彻底理解两种搜索策略的本质差异。

一、算法核心思想对比

深度优先搜索(DFS)
  • 遍历逻辑:类似“迷宫探索”,沿单一路径深入到底,回溯后继续探索其他分支

  • 数据结构:递归栈或显式栈(后进先出)

  • 核心特征

    • 可能陷入深层次分支(需设置终止条件)

    • 空间复杂度低(仅存储单条路径)

    • 适用于“探索所有可能性”的场景

广度优先搜索(BFS)
  • 遍历逻辑:类似“水波扩散”,逐层扫描所有相邻节点

  • 数据结构:队列(先进先出)

  • 核心特征

    • 首次访问即得最短路径(无权图)

    • 空间复杂度较高(存储整层节点)

    • 适用于“寻找最近解”的场景

二、算法实现模板

DFS模板(递归 vs 显式栈)
// 递归实现(以二叉树为例)
void dfs(TreeNode* node) {
    if (!node) return;
    // 前序操作
    dfs(node->left);
    // 中序操作
    dfs(node->right);
    // 后序操作
}

// 显式栈实现(图遍历)
void dfsStack(vector<vector<int>>& graph, int start) {
    stack<int> stk;
    vector<bool> visited(graph.size(), false);
    stk.push(start);
    visited[start] = true;

    while (!stk.empty()) {
        int u = stk.top();
        stk.pop();
        for (int v : graph[u]) {
            if (!visited[v]) {
                visited[v] = true;
                stk.push(v); // 注意压栈顺序与遍历顺序的关系
            }
        }
    }
}

BFS模板(队列实现) 

void bfs(vector<vector<int>>& graph, int start) {
    queue<int> q;
    vector<bool> visited(graph.size(), false);
    q.push(start);
    visited[start] = true;

    while (!q.empty()) {
        int size = q.size();
        for (int i = 0; i < size; ++i) { // 层级遍历关键
            int u = q.front();
            q.pop();
            for (int v : graph[u]) {
                if (!visited[v]) {
                    visited[v] = true;
                    q.push(v);
                }
            }
        }
    }
}

三、经典问题实战

例题1:二叉树层序遍历(BFS应用)
vector<vector<int>> levelOrder(TreeNode* root) {
    vector<vector<int>> res;
    if (!root) return res;
    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
        int size = q.size();
        vector<int> level;
        for (int i=0; i<size; ++i) {
            auto node = q.front(); q.pop();
            level.push_back(node->val);
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
        res.push_back(level);
    }
    return res;
}
例题2:岛屿数量(DFS应用)
void dfs(vector<vector<char>>& grid, int i, int j) {
    if (i<0 || i>=grid.size() || j<0 || j>=grid[0].size() || grid[i][j] != '1') 
        return;
    grid[i][j] = '0'; // 标记为已访问
    dfs(grid, i+1, j);
    dfs(grid, i-1, j);
    dfs(grid, i, j+1);
    dfs(grid, i, j-1);
}

int numIslands(vector<vector<char>>& grid) {
    int count = 0;
    for (int i=0; i<grid.size(); ++i) {
        for (int j=0; j<grid[0].size(); ++j) {
            if (grid[i][j] == '1') {
                dfs(grid, i, j);
                ++count;
            }
        }
    }
    return count;
}

四、复杂度与适用场景对比

维度DFSBFS
时间复杂度O(V + E)O(V + E)
空间复杂度O(h)(h为递归深度)O(w)(w为最大层宽)
最短路径不保证(除非记录路径)天然保证(无权图)
适用场景连通性检测、回溯问题层序处理、最短路径问题
内存风险递归深度过大可能栈溢出层级过宽可能内存爆炸

五、大厂真题解析

真题1:迷宫最短路径

题目描述
给定N×M网格迷宫(0可走,1为墙),求从起点到终点的最短步数。

BFS解法关键点

  • 使用队列记录坐标与步数

  • 方向数组简化代码

  • 提前终止条件(到达终点)

int shortestPath(vector<vector<int>>& maze, vector<int>& start, vector<int>& end) {
    vector<vector<int>> dirs = {{-1,0}, {1,0}, {0,-1}, {0,1}};
    queue<pair<int, int>> q;
    q.push({start[0], start[1]});
    maze[start[0]][start[1]] = 1; // 标记为已访问
    int steps = 0;

    while (!q.empty()) {
        int size = q.size();
        while (size--) {
            auto [x, y] = q.front(); q.pop();
            if (x == end[0] && y == end[1]) return steps;
            for (auto& d : dirs) {
                int nx = x + d[0], ny = y + d[1];
                if (nx>=0 && nx<maze.size() && ny>=0 && ny<maze[0].size() 
                    && maze[nx][ny] == 0) {
                    maze[nx][ny] = 1;
                    q.push({nx, ny});
                }
            }
        }
        ++steps;
    }
    return -1;
}
真题2:括号生成

DFS解法核心

  • 记录剩余左右括号数量

  • 剪枝条件(右括号剩余数 < 左括号时无效)

void generate(vector<string>& res, string path, int left, int right) {
    if (left == 0 && right == 0) {
        res.push_back(path);
        return;
    }
    if (left > 0) generate(res, path + "(", left-1, right);
    if (right > left) generate(res, path + ")", left, right-1);
}

六、常见误区与优化技巧

  1. DFS栈溢出:递归深度过大时改用显式栈或迭代加深

  2. BFS状态爆炸:使用双向BFS或A*启发式搜索优化

  3. 重复访问判断:必须在入队/入栈时标记访问(而非出队时)

  4. 剪枝策略:根据问题特性提前终止无效分支(如超过当前最优解)

  5. 状态压缩:使用位运算减少内存消耗(如N皇后问题)


LeetCode真题练习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值