数据结构与算法——深度优先搜索(DFS)和广度优先搜索(BFS)

目录

引言

深度优先搜索(DFS)

广度优先搜索(BFS)

区别与联系

岛屿数量(LeetCode 200)

经典例题


引言

深度优先搜索(DFS)

定义与原理

  • 深度优先搜索是一种用于遍历或搜索树或图的算法。它从根节点(或起始节点)开始,沿着树的深度遍历树的节点,尽可能深地搜索树的分支。

  • 当节点v的所在边都已被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这个过程一直进行到已发现从源节点可达的所有节点为止。

  • 深度优先搜索通常使用递归或栈来实现。

关键点

  • 递归与回溯:DFS通过递归函数或栈来实现,当一条路走不通时,会回溯到上一个节点,尝试其他路径。

  • 深度优先:每次尽可能深地遍历图或树,直到无法再深入。

应用场景

  • 迷宫问题:在迷宫中寻找从起点到终点的路径。

  • 图的连通性问题:判断图中的所有节点是否都连通。

  • 拓扑排序:在有向无环图(DAG)中进行拓扑排序。


广度优先搜索(BFS)

定义与原理

  • 广度优先搜索是一种用于遍历或搜索树或图的算法。它从根节点(或起始节点)开始,先访问离根节点最近的节点,然后逐层向下访问。

  • 它通过队列来实现,队列中保存的是待访问的节点。

关键点

  • 队列:BFS使用队列来保存待访问的节点,按照先进先出的原则进行访问。

  • 广度优先:每次尽可能广地遍历图或树的一层节点。

应用场景

  • 最短路径问题:在无权图或有权图中寻找从一个顶点到另一个顶点的最短路径。

  • 社交网络中的人际关系分析:分析社交网络中的好友关系,找出两个用户之间的最短路径。

  • 层次遍历:在树形结构中进行层次遍历。


区别与联系

区别

  • 搜索策略:DFS是深度优先,一条路走到底;BFS是广度优先,逐层遍历。
  • 数据结构:DFS通常使用递归或栈;BFS使用队列。
  • 完备性:BFS是一种完备搜索,只要问题有解就一定能找到;DFS则可能陷入无限循环,不是完备搜索。
  • 适用性:DFS适合深度较大、目标节点层次较浅的情况;BFS适合目标节点层次较深或需要找到最短路径的情况。

联系

  • 两者都是图论和树形结构中的基本搜索算法。
  • 在某些情况下,可以相互转换或结合使用以达到更好的搜索效果。例如,在解决某些复杂问题时,可以先使用BFS找到可能的解空间,再使用DFS进行深入搜索。

岛屿数量(LeetCode 200)

题目描述
给定一个由 '1'(陆地)和 '0'(水)组成的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设整个网格的四周都被水包围着。

示例

输入:  
11110  
11010  
11000  
00000  
  
输出: 1
输入:  
11000  
11000  
00100  
00011  
  
输出: 3

深度优先搜索(DFS)解题思路

  1. 遍历网格:首先,我们需要遍历整个网格,对于每个遇到的 '1'(陆地),我们将其视为一个岛屿的起点。

  2. 深度优先搜索:当我们找到一个岛屿的起点时,我们启动DFS来遍历这个岛屿的所有陆地,并将它们标记为已访问(例如,将它们更改为 '0' 或其他非陆地标记)。这样可以确保我们不会重复计算同一个岛屿。

  3. 计数:每当我们通过DFS遍历完一个岛屿的所有陆地后,我们就将岛屿的计数增加1。

  4. 继续遍历:我们继续遍历网格,直到我们检查完所有的格子。

#include <vector>  
using namespace std;  
  
class Solution {  
public:  
    int numIslands(vector<vector<char>>& grid) {  
        if (grid.empty() || grid[0].empty()) return 0;  
        int rows = grid.size();  
        int cols = grid[0].size();  
        int count = 0;  
  
        for (int i = 0; i < rows; ++i) {  
            for (int j = 0; j < cols; ++j) {  
                if (grid[i][j] == '1') {  
                    dfs(grid, i, j);  
                    ++count;  
                }  
            }  
        }  
  
        return count;  
    }  
  
private:  
    void dfs(vector<vector<char>>& grid, int i, int j) {  
        int rows = grid.size();  
        int cols = grid[0].size();  
  
        // 检查边界条件和当前格子是否为陆地  
        if (i < 0 || j < 0 || i >= rows || j >= cols || 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); // 右  
    }  
};

广度优先搜索(BFS)解题思路
使用BFS来遍历整个网格。当我们遇到一个'1'时,表示发现了一个岛屿,我们从这个'1'开始进行BFS遍历,将所有与之相连的'1'都标记为已访问(比如,可以标记为'0'或另一个不同的字符)。在BFS的过程中,我们不断地将相邻的'1'加入队列中,并继续遍历,直到队列为空。每完成一次BFS遍历,岛屿数量就加一。最后返回岛屿的总数.

#include <vector>  
#include <queue>  
using namespace std;  
  
class Solution {  
public:  
    int numIslands(vector<vector<char>>& grid) {  
        if (grid.empty() || grid[0].empty()) return 0;  
        int rows = grid.size();  
        int cols = grid[0].size();  
        int count = 0;  
          
        for (int i = 0; i < rows; ++i) {  
            for (int j = 0; j < cols; ++j) {  
                if (grid[i][j] == '1') {  
                    ++count;  
                    bfs(grid, i, j);  
                }  
            }  
        }  
          
        return count;  
    }  
      
private:  
    void bfs(vector<vector<char>>& grid, int row, int col) {  
        int rows = grid.size();  
        int cols = grid[0].size();  
        vector<pair<int, int>> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右四个方向  
        queue<pair<int, int>> q;  
          
        // 将起始点加入队列,并标记为已访问  
        q.push({row, col});  
        grid[row][col] = '0';  
          
        while (!q.empty()) {  
            auto curr = q.front();  
            q.pop();  
            int x = curr.first;  
            int y = curr.second;  
              
            // 遍历四个方向  
            for (auto& dir : directions) {  
                int nx = x + dir.first;  
                int ny = y + dir.second;  
                  
                // 检查新坐标是否越界且为陆地  
                if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && grid[nx][ny] == '1') {  
                    // 加入队列并标记为已访问  
                    q.push({nx, ny});  
                    grid[nx][ny] = '0';  
                }  
            }  
        }  
    }  
};

经典例题

深度优先搜索(DFS)

  1. 130. 被围绕的区域
  2. 200. 岛屿数量
  3. 301. 删除无效的括号
  4. 513. 找树左下角的值
  5. 515. 在每个树行中找最大值
  6. 695. 岛屿的最大面积
  7. 733. 图像渲染
  8. 797. 所有可能的路径
  9. 980. 不同路径 III
  10. 127. 单词接龙

广度优先搜索(BFS)

  1. 2. 两数相加 II
  2. 54. 螺旋矩阵 II
  3. 75. 颜色分类
  4. 102. 二叉树的层序遍历
  5. 126. 单词接龙 II
  6. 127. 单词接龙(虽然DFS也可以解决,但BFS通常更直观)
  7. 199. 二叉树的右视图
  8. 207. 课程表
  9. 279. 完全平方数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值