5、一切皆可搜索(结合LeetCode 695、547、417、46、77、79、51、934、126、130、257、47、40、37、310学习)

作者学习算法的教材是LeetCode 101

有需要的同学直接GitHub搜索LeetCode 101即可 **

深度优先搜索(DFS)

用到栈结构或递归,先入后出;可以用来检测是否有环路。

695. 岛屿的最大面积(难度:中等)

  • 使用深度优先遍历算法dfs,用directed表示方向,每次深度优先遍历算法中都要判断当前节点的上下左右四个位置;
  • 代码如下:
class Solution {
    vector<int> direction{-1 , 0 , 1 , 0 , -1};
public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        if(grid.empty() || grid[0].empty()) {
            return 0;
        }
        int max_area = 0;
        for(int i = 0 ; i < grid.size() ; i++) {
            for(int j = 0 ; j < grid[i].size() ; j++) {
                if(grid[i][j] == 1) {
                    max_area = max(max_area , dfs(grid , i , j));
                }
            }
        }
        return max_area;
    }

    int dfs(vector<vector<int>>& grid , int r , int c) {
        if(grid[r][c] == 0) {
            return 0;
        }
        grid[r][c] = 0;
        int x , y , area = 1;
        for(int i = 0 ; i < 4 ; i++) {
            x = r + direction[i];
            y = c + direction[i + 1];
            if(x < grid.size() && y < grid[0].size()) {
                area += dfs(grid , x , y);
            }
        }
        return area;
    }
};

547. 省份数量(难度:中等)

  • 使用visited记录结点是否被访问过,接着用深度优先遍历算法dfs找到同一个省的所有城市;
  • 代码如下:
class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        vector<bool> visited(isConnected.size() , false);
        int count = 0;
        for(int i = 0 ; i < isConnected.size() ; i++) {
            if(!visited[i]) {
                dfs(isConnected , i ,visited);
                count++;
            }
        }
        return count;
    }

    void dfs(vector<vector<int>>& isConnected , int i , vector<bool>& visited) {
        visited[i] = true;
        for(int k = 0 ; k < isConnected.size() ; k++) {
            if(isConnected[i][k] == 1 && !visited[k]) {
                dfs(isConnected , k , visited);
            }
        }
    }
};

417. 太平洋大西洋水流问题(难度:中等)

  • 题目要求输出能到达大西洋和太平洋的顶点,若对所有节点进行搜索,复杂度很高,所以可以从太平洋和大西洋出发,逆着找到这些顶点;
  • 分别用两个二维数组保存节点状态(分别是从太平洋出发和大西洋)出发,如果最后两个二维数组中同一个节点均为true,说明该节点可以到达太平洋和大西洋,输出该节点;
  • 代码如下:
class Solution {
    vector<int> direction{-1 , 0 , 1 , 0 , -1};
public:
    vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
        if(heights.empty() || heights[0].empty()) {
            return heights;
        }
        vector<vector<int>> ans;
        vector<vector<bool>> can_reach_p(heights.size() , vector<bool>(heights[0].size() , false)); 
        vector<vector<bool>> can_reach_a(heights.size() , vector<bool>(heights[0].size() , false));
        for(int i = 0 ; i < heights.size() ; i++) {
            dfs(heights , can_reach_p , i , 0);
            dfs(heights , can_reach_a , i , heights[0].size() - 1);
        }
        for(int i = 0 ; i < heights[0].size() ; i++) {
            dfs(heights , can_reach_p , 0 , i);
            dfs(heights , can_reach_a , heights.size() - 1 , i);
        } 
        for(int i = 0 ; i < heights.size() ; i++) {
            for(int j = 0 ; j < heights[0].size() ; j++) {
                if(can_reach_a[i][j] && can_reach_p[i][j]) {
                    ans.push_back(vector<int>{i , j});
                }
            }
        }
        return ans;
    }

    void dfs(vector<vector<int>>& heights , vector<vector<bool>>& can_reach , int r , int c) {
        if(can_reach[r][c]) {
            return;
        }
        can_reach[r][c] = true;
        int x , y;
        for(int i = 0 ; i < 4 ; i++) {
            x = r + direction[i];
            y = c + direction[i + 1];
            if(x >= 0 && x < heights.size() && y >= 0 && y < heights[0].size() && heights[x][y] >= heights[r][c]) {
                dfs(heights , can_reach , x ,y);
            }
        }
    }
};

回溯法(先改变,后还原)

46. 全排列(难度:中等)

  • 由于前后顺序不同算两种排列,所以递归时用level+1而不是i+1,这样可以遍历到当前节点前的数字;
  • 代码如下:
class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> res;
        backtracking(nums , 0 , res);
        return res;
    }

    void backtracking(vector<int>& nums , int level , vector<vector<int>>& res) {
        if(level == nums.size() - 1) {
            res.push_back(nums);
            return;
        }
        for(int i = level ; i < nums.size() ; i++) {
            swap(nums[i] , nums[level]);
            backtracking(nums , level + 1 , res);
            swap(nums[i] , nums[level]);
        }
    }
};

77. 组合(难度:中等)

  • 回溯是否把当前的数字加入到结果中;
  • 前后顺序不同算一种组合,所以递归时用i+1而不是pos+1;
  • 代码如下:
class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int>> res;
        vector<int> comb(k , 0);
        int count = 0;
        backtracting(res , comb , count , 1 , n , k);
        return res;
    }

    void backtracting(vector<vector<int>>& res , vector<int>& comb , int count , int pos , int n , int k) {
        if(count == k) {
            res.push_back(comb);
            return;
        }
        for(int i = pos ; i <= n ; i++) {
            comb[count++] = i;
            backtracting(res , comb , count , i + 1 , n , k);
            count--;
        }
    }
};

79. 单词搜索(难度:中等)

  • 使用visited记录节点是否被查看过,find记录单词是否被找到,从给定的数组中的每一个元素出发,判断是否存在给定单词;
  • 代码如下:
class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        if(board.empty()) {
            return false;
        }
        vector<vector<bool>> visited(board.size() , vector<bool>(board[0].size() , false));
        bool find = false;
        for(int i = 0 ; i < board.size() ; i++) {
            for(int j = 0 ; j < board[0].size() ; j++) {
                backtracking(board , i , j , word , 0 , find , visited);
            }
        }
        return find;
    }

    void backtracking(vector<vector<char>>& board , int i , int j , string& word , int pos , bool& find , vector<vector<bool>>& visited) {
        if(i < 0 || i >= board.size() || j < 0 || j >= board[0].size()) {
            return;
        }
        if(visited[i][j] || find || board[i][j] != word[pos]) {
            return;
        }
        if(pos == word.size() - 1) {
            find = true;
            return;
        }
        visited[i][j] = true;
        backtracking(board , i , j + 1 , word , pos + 1 , find , visited);
        backtracking(board , i , j - 1 , word , pos + 1 , find , visited);
        backtracking(board , i + 1 , j , word , pos + 1 , find , visited);
        backtracking(board , i - 1 , j , word , pos + 1 , find , visited);
        visited[i][j] = false;
    }
};

51. N皇后(难度:困难)

  • 使用visited记录节点是否被查看过
  • 代码如下:

广度优先搜索(BFS)

通过队列实现

934 . 最短的桥(难度:中等)

  • 先到找第一个岛屿(即值为1的点),然后通过深度优先遍历算法找到与该点相连的所有的点,这样就找的了一个岛屿,在深度优先算法遍历的过程中,把第一个岛屿的值都变为2、把值为0的点都加入到队列points中,这样是为了与第二个岛屿区分开,数组中就只剩下了0、1、2三种数字;
  • 然后遍历队列points,先记录队列的初始长度,用count记录需要改变的0的数量,遍历队列中各个点的上下左右位置,遇到值为2的点,继续循环,遇到值为1的点,说明找到了第二个岛屿,返回count,如果遇到值为0的点,将该点加入到队列中,并将该点的值改为2;(因为初始队列中都是与第一个岛屿相邻的点)
  • 如果遍历完初始长度的points队列未结束,开始继续遍历points,方法与上面相同,但count要加一;
  • 代码如下:
class Solution {
    vector<int> direction{-1 , 0 , 1 , 0 , -1};
public:
    int shortestBridge(vector<vector<int>>& A) {
        int m = A.size();
        int n = A[0].size();
        queue<pair<int , int>> points;
        bool flipped = false;
        for(int i = 0 ; i < m ; i++) {
            if(flipped) {
                break;
            }
            for(int j = 0 ; j < n ; j++) {
                if(A[i][j] == 1) {
                    dfs(points , A , m , n , i , j);
                    flipped = true;
                    break;
                }
            }
        }
        int x , y;
        int count = 0;
        while(!points.empty()) {
            count++;
            int n_points = points.size();
            while(n_points--) {
                auto [r , c] = points.front();
                points.pop();
                for(int k = 0 ; k < 4 ; k++) {
                    x = r + direction[k];
                    y = c + direction[k + 1];
                    if(x >= 0 && y >= 0 && x < m && y < n) {
                        if(A[x][y] == 2) {
                            continue;
                        }
                        if(A[x][y] == 1) {
                            return count;
                        }
                        points.push({x , y});
                        A[x][y] = 2;
                    }
                }
            }
        }
        return 0;
    }

    void dfs(queue<pair<int , int>>& points , vector<vector<int>>& A , int m , int n , int i , int j) {
        if(i < 0 || i >= m || j < 0 || j >= n || A[i][j] == 2) {
            return;
        }
        if(A[i][j] == 0) {
            points.push({i , j});
            return;
        }
        A[i][j] = 2;
        dfs(points , A , m , n , i , j + 1);
        dfs(points , A , m , n , i , j - 1);
        dfs(points , A , m , n , i + 1 , j);
        dfs(points , A , m , n , i - 1 , j);
    }
};

126 . 单词接龙Ⅱ(难度:困难)

  • 使用visited记录节点是否被查看过
  • 代码如下:

130 . 被围绕的区域(难度:中等)

  • 与上文中的太平洋、大西洋问题思路一致;
  • 由于边上的O不会被改变,所以与边上O相连的O也不会被改变,从数组的四个边出发,找到所有从边界出发的O,标为false(不改变),最后遍历整个数组,如果遇到O且标记为true,则变为X;
  • 代码如下:
class Solution {
    vector<int> direction{-1 , 0 , 1 , 0 , -1};
public:
    void solve(vector<vector<char>>& board) {
        int m = board.size();
        int n = board[0].size();
        vector<vector<bool>> check(m , vector<bool>(n , true));
        for(int i = 0 ; i < m ; i++) {
            if(board[i][0] == 'O') {
                dfs(board , check , m , n , i , 0);
            }
            if(board[i][n - 1] == 'O') {
                dfs(board , check , m , n , i , n - 1);
            }
        }
        for(int i = 0 ; i < n ; i++) {
            if(board[0][i] == 'O') {
                dfs(board , check , m , n , 0 , i);
            }
            if(board[m - 1][i] == 'O') {
                dfs(board , check , m , n , m - 1 , i);
            }
        }
        for(int i = 0 ; i < m ; i++) {
            for(int j = 0 ; j < n ; j++) {
                if(check[i][j] && board[i][j] == 'O') {
                    board[i][j] = 'X';
                }
            }
        }
    }

    void dfs(vector<vector<char>>& board , vector<vector<bool>>& check , int m , int n , int i , int j) {
        if(i < 0 || i >= m || j < 0 || j >= n) {
            return;
        }
        if(board[i][j] != 'O') {
            return;
        }
        if(!check[i][j]) {
            return;
        }
        check[i][j] = false;
        int x , y;
        for(int k = 0 ; k < 4 ; k++) {
            x = i + direction[k];
            y = j + direction[k + 1];
            dfs(board , check , m , n , x , y);
        }
    }
};

257 . 二叉树的所有路径(难度:简单)

  • 深度优先遍历算法,遇到叶子节点将路径加入到返回数组res中;
  • 代码如下:
class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        if(root == nullptr) {
            return vector<string>{};
        }
        vector<string> res;
        out(root , res , "");
        return res;
    }

    void out(TreeNode* root , vector<string>& res , string path) {
        path += to_string(root->val);
        if(root->left == nullptr && root->right == nullptr) {
            res.push_back(path);
            return;
        }
        if(root->left) {
            out(root->left , res , path + "->");
        }
        if(root->right) {
            out(root->right , res , path + "->");
        }
    }
};

47 . 单词接龙Ⅱ(难度:困难)

  • 使用visited记录节点是否被查看过
  • 代码如下:

40 . 单词接龙Ⅱ(难度:困难)

  • 使用visited记录节点是否被查看过
  • 代码如下:

37 . 单词接龙Ⅱ(难度:困难)

  • 使用visited记录节点是否被查看过
  • 代码如下:

310 . 单词接龙Ⅱ(难度:困难)

  • 使用visited记录节点是否被查看过
  • 代码如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值