Leetcode刷题:搜索算法

一、算法核心内容

1.1深度优先搜索

1、概念:深度优先搜索(depth-first seach,DFS)在搜索到一个新的节点时,立即对该新节点进行遍历;因此遍历需要用先入后出的栈来实现,也可以通过与栈等价的递归来实现。对于树结构而言,由于总是对新节点调用遍历,因此看起来是向着“深”的方向前进。
2、举例:1—>2—>4—>3
请添加图片描述
3、作用:检测环路,记录每个遍历过的节点的父节点,若一个节点被再次遍历且父节点不同,则说明有环。
4、状态记录:对已经搜索过的节点进行标记,以防止在遍历时重复搜索某个节点,这种做法叫做状态记录。

1.2回溯法

1、回溯法(backtracking)是优先搜索的一种特殊情况,又称为试探法,常用于需要记录节点状态的深度优先搜索。通常来说,排列、组合、选择类问题使用回溯法比较方便。
2、在搜索到某一节点的时候,如果我们发现目前的节点(及
其子节点)并不是需求目标时,我们回退到原来的节点继续搜索,并且把在目前节点修改的状态还原。
3、优点:我们可以始终只对图的总状态进行修改,而非每次遍历时新建一个图来储存状态。
4、步骤: [修改当前节点状态]→[递归子节点]→[回改当前节点状态]。
5、修改情况:回溯法修改一般有两种情况,一种是修改最后一位输出,比如排列组合;一种是修改访问标记,比如矩阵里搜字符串。

1.3广度优先搜索

1、概念:广度优先搜索(breadth-first search,BFS)是一层层进行遍历的,因此需要用先入先出的队列而非先入后出的栈进行遍历。由于是按层次进行遍历,广度优先搜索时按照“广”的方向进行遍历的,也常常用来处理最短路径等问题。
2、考虑如下一颗简单的树。我们从 1 号节点开始遍历,假如遍历顺序是从左子节点到右子节点,那么按照优先向着“广”的方向前进的策略,队列顶端的元素变化过程为 [1]->[2->3]->[4],其中方括号代表每一层的元素。
在这里插入图片描述

二、深度优先搜索题目

2.1Leetcode695

2.1.1题目描述

给定一个包含了一些 0 和 1 的非空二维数组 grid 。
一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0

2.1.2输入输出格式

[[0,0,1,0,0,0,0,1,0,0,0,0,0],
 [0,0,0,0,0,0,0,1,1,1,0,0,0],
 [0,1,1,0,1,0,0,0,0,0,0,0,0],
 [0,1,0,0,1,1,0,0,1,0,1,0,0],
 [0,1,0,0,1,1,0,0,1,1,1,0,0],
 [0,0,0,0,0,0,0,0,0,0,1,0,0],
 [0,0,0,0,0,0,0,1,1,1,0,0,0],
 [0,0,0,0,0,0,0,1,1,0,0,0,0]]
对于上面这个给定矩阵应返回 6。注意答案不应该是 11 ,因为岛屿只能包含水平或垂直的四个方向的 1

2.1.3代码

class Solution {
public:
    int direction[5]={1,0,-1,0,1};
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        if(grid.size()==0 || grid[0].size()==0) return 0;
        int ans=0;
        for(int i=0;i<grid.size();i++){
            for(int j=0;j<grid[0].size();j++){
                ans=max(ans,dfs(grid,i,j));
            }
        }
        return ans;
    }
    int dfs(vector<vector<int>>& grid,int row,int column){
        if(grid[row][column]==0) return 0;
        int area=1;
        grid[row][column]=0;
        for(int i=0;i<4;i++){
            int x=row+direction[i];
            int y=column+direction[i+1];
            if(x>=0 && y>=0 && x<grid.size() && y<grid[0].size()){
                area+=dfs(grid,x,y);
            }
        }
        return area;
    }
};

2.2Leetcode547

2.2.1题目描述

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。

2.2.2输入输出格式

请添加图片描述

2.2.3代码

class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        if(isConnected.size()==0 || isConnected[0].size()==0) return 0;
        int n=isConnected.size();
        int cnt=0;
        vector<bool> flag(n,false);
        for(int i=0;i<n;i++){
            if(!flag[i]){
                dfs(isConnected,i,flag);
                cnt++;
            }
        }
        return cnt;
    }
    void dfs(vector<vector<int>>& isConnected,int n, vector<bool>& flag){
        flag[n]=true;
        for(int i=0;i<isConnected.size();i++){
            int temp=isConnected[n][i];
            if(temp==1 && !flag[i]){
                dfs(isConnected,i,flag);
            }
        }
    }
};

2.3Leetcode417

2.3.1题目描述

给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。
规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。
请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。

2.3.2输入输出格式

给定下面的 5x5 矩阵:

  太平洋 ~   ~   ~   ~   ~ 
       ~  1   2   2   3  (5) *
       ~  3   2   3  (4) (4) *
       ~  2   4  (5)  3   1  *
       ~ (6) (7)  1   4   5  *
       ~ (5)  1   1   2   4  *
          *   *   *   *   * 大西洋

返回:
[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (上图中带括号的单元).

2.3.3代码

class Solution {
public:
    int direction[5]={1,0,-1,0,1};
    vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
        if(heights.size()==0 || heights[0].size()==0) {
            return {};
        }
        vector<vector<int>> ans;
        int m=heights.size(),n=heights[0].size();
        //太平洋
        vector<vector<bool>> can_reachp(m,vector<bool>(n,false));
        //大西洋
        vector<vector<bool>> can_reacha(m,vector<bool>(n,false));
        for(int i=0;i<m;i++){
            dfs(heights,can_reachp,i,0);
            dfs(heights,can_reacha,i,n-1);
        }
        for(int i=0;i<n;i++){
            dfs(heights,can_reachp,0,i);
            dfs(heights,can_reacha,m-1,i);
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(can_reacha[i][j] && can_reachp[i][j]){
                    ans.push_back(vector<int>{i,j});
                }
            }
        }
        return ans;
    }
    void dfs(vector<vector<int>>& heights,vector<vector<bool>>& can_reach,int row,int column){
        if(can_reach[row][column]) return;
        can_reach[row][column]=true;
        for(int i=0;i<4;i++){
            int x=row+direction[i];
            int y=column+direction[i+1];
            if(x>=0 && y>=0 && x<heights.size() && y<heights[0].size() && heights[row][column]<=heights[x][y]){
                dfs(heights,can_reach,x,y);
            }
        }
    }
};

2.4Leetcode419

2.4.1题目描述

给定一个二维的甲板, 请计算其中有多少艘战舰。 战舰用 'X’表示,空位用 '.'表示。 你需要遵守以下规则:
给你一个有效的甲板,仅由战舰或者空位组成。
战舰只能水平或者垂直放置。换句话说,战舰只能由 1xN (1 行, N 列)组成,或者 Nx1 (N 行, 1 列)组成,其中N可以是任意大小。
两艘战舰之间至少有一个水平或垂直的空位分隔 - 即没有相邻的战舰。

2.4.2输入输出格式

请添加图片描述

2.4.3代码

class Solution {
public:
    int direction[5]={1,0,-1,0,1};
    int countBattleships(vector<vector<char>>& board) {
        if(board.size()==0 || board[0].size()==0) return 0;
        int cnt=0;
        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[0].size();j++){
                if(board[i][j]=='X'){
                    dfs(board,i,j);
                    cnt++;
                }
            }
        }
        return cnt;
    }
    void dfs(vector<vector<char>>& board,int row,int column){
        if(board[row][column]=='.') return;
        board[row][column]='.';
        for(int i=0;i<4;i++){
            int x=row+direction[i];
            int y=column+direction[i+1];
            if(x>=0 && y>=0 && x<board.size() && y<board[0].size()){
                dfs(board,x,y);
            }
        }
    }
};

2.5Leetcode200

2.5.1题目描述

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。

2.5.2输入输出格式

输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

2.5.3代码

class Solution {
public:
    int direction[5]={1,0,-1,0,1};
    int numIslands(vector<vector<char>>& grid) {
        if(grid.size()==0 || grid[0].size()==0) return 0;
        int cnt=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);
                    cnt++;
                }
            }
        }
        return cnt;
    }
    void dfs(vector<vector<char>>& grid,int row,int column){
        if(grid[row][column]=='0') return;
        grid[row][column]='0';
        for(int i=0;i<4;i++){
            int x=row+direction[i];
            int y=column+direction[i+1];
            if(x>=0 && y>=0 && x<grid.size() && y<grid[0].size()){
                dfs(grid,x,y);
            }
        }
    }
};

2.6Leetcode130

2.6.1题目描述

给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ ,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

2.6.2输入输出格式

在这里插入图片描述

2.6.3代码

class Solution {
public:
    int direction[5]={1,0,-1,0,1};
    void solve(vector<vector<char>>& board) {
        int m=board.size();
        int n=board[0].size();
        vector<vector<bool>> flag(m,vector<bool>(n,false));
        for(int i=0;i<m;i++){
            if(board[i][0]=='O' && !flag[i][0]) dfs(board,flag,i,0);
            if(board[i][n-1]=='O' && !flag[i][n-1]) dfs(board,flag,i,n-1);
        }
        for(int i=0;i<n;i++){
            if(board[0][i]=='O' && !flag[0][i]) dfs(board,flag,0,i);
            if(board[m-1][i]=='O' && !flag[m-1][i]) dfs(board,flag,m-1,i);
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(board[i][j]=='O' && !flag[i][j]){
                    board[i][j]='X';
                }
            }
        }
    }
    void dfs(vector<vector<char>>& board,vector<vector<bool>>& flag,int row,int column){
        if(flag[row][column] || board[row][column]=='X') return;
        flag[row][column]=true;
        for(int i=0;i<4;i++){
            int x=row+direction[i];
            int y=column+direction[i+1];
            if(x>=0 && y>=0 && x<board.size() && y<board[0].size()){
                dfs(board,flag,x,y);
            }
        }
    }
};

2.7Leetcode257

2.7.1题目描述

给定一个二叉树,返回所有从根节点到叶子节点的路径。

2.7.2输入输出格式

在这里插入图片描述

2.7.3代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> ans;
        string temp;
        dfs(root,ans,temp);
        return ans;
    }
    void dfs(TreeNode* root,vector<string>& ans,string temp){
        if(root!=nullptr){
            temp+=to_string(root->val);
            if((root->left==nullptr)&&(root->right==nullptr)){
                ans.push_back(temp);
                return ;
            }else{
                temp+="->";
                dfs(root->left,ans,temp);
                dfs(root->right,ans,temp);
            }
        }
        return;
    
    }
};

三、回溯法

3.1Leetcode46

3.1.1题目描述

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

3.1.2输入输出格式

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

3.1.3代码

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> ans;
        track(nums,0,ans);
        return ans;

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

3.2Leetcode77

3.2.1题目描述

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。

3.2.2输入输出格式

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

3.2.3代码

class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int>> ans;
        vector<int> temp(k,0);
        int count=0;
        backtracking(ans,temp,count,1,n,k);
        return ans;
    }
    void backtracking(vector<vector<int>>& ans,vector<int>& temp,int count,int pos,int n,int k){
        if(count==k){
            ans.push_back(temp);
            return;
        }
        for(int i=pos;i<=n;i++){
            temp[count++]=i;
            backtracking(ans,temp,count,i+1,n,k);
            count--;
        }
    }
};

3.3Leetcode79

3.3.1题目描述

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

3.3.2输入输出格式

在这里插入图片描述

3.3.3代码

class Solution {
public:
    int direction[5]={1,0,-1,0,1};
    bool exist(vector<vector<char>>& board, string word) {
        if(board.empty()){
            return false;
        }
        int m=board.size(),n=board[0].size();
        vector<vector<bool>> flag(m,vector<bool>(n,false));
        bool ans=false;
        int size=word.size();
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                backtracking(board,flag,word,0,i,j,ans);
            }
        }
        return ans;
    }

    void backtracking(vector<vector<char>>& board,vector<vector<bool>>& flag,string word,int pos,int row,int column,bool& ans){
        if(board[row][column]!=word[pos] || flag[row][column] || ans){
            return;
        }
        if(pos==word.size()-1){//返回正确值的if
            ans=true;
            return;
        }
        flag[row][column]=true;
        for(int i=0;i<4;i++){
            int x=row+direction[i];
            int y=column+direction[i+1];
            if(x>=0 && y>=0 && x<board.size() && y<board[0].size() && !flag[x][y]){
                backtracking(board,flag,word,pos+1,x,y,ans);
            }
        }
        flag[row][column]=false;//回溯
    }
};

3.4Leetcode51

3.4.1题目描述

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。

3.4.2输入输出格式

在这里插入图片描述

3.4.3代码

class Solution {
public:
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> ans;//存储最终结果
        vector<string> temp(n,string(n,'.'));//存储每一个解
        backtracking(ans,temp,0,n);//回溯
        return ans;//返回结果
    }
    //row:遍历每一行
    //n:总行数
    void backtracking(vector<vector<string>>& ans,vector<string>& temp,int row,int n){
        if(row==n){
            ans.push_back(temp);
            return;
        }
        for(int col=0;col<n;col++){
            if(isvalid(row,col,temp,n)){
                temp[row][col]='Q';
                backtracking(ans,temp,row+1,n);
                temp[row][col]='.';
            }
        }
    }
    //判断是否符合下棋条件
    bool isvalid(int row,int col,vector<string>& temp,int n){
        for(int i=0;i<row;i++){
            if(temp[i][col]=='Q') return false;
        }
        for(int i=row-1,j=col-1;i>=0 && j>=0;i--,j--){
            if(temp[i][j]=='Q') return false;
        }
        for(int i=row-1,j=col+1;i>=0 && j<n;i--,j++){
            if(temp[i][j]=='Q') return false;
        }
        return true;
    }
};

四、广度优先搜索

4.1Leetcode934

4.1.1题目描述

在给定的二维二进制数组 A 中,存在两座岛。(岛是由四面相连的 1 形成的一个最大组。)
现在,我们可以将 0 变为 1,以使两座岛连接起来,变成一座岛。
返回必须翻转的 0 的最小数目。(可以保证答案至少是 1 。)

4.1.2输入输出格式

输入:A = [[0,1,0],[0,0,0],[0,0,1]]
输出:2

4.1.3代码

class Solution {
public:
    int direction[5]={1,0,-1,0,1};
    int shortestBridge(vector<vector<int>>& grid) {
        //dfs查找第一个岛屿,并将其置2.
        int m=grid.size(),n=grid[0].size();
        queue<pair<int,int>> points;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]==1){
                    dfs(grid,i,j,points);
                    goto next;
                }
            }
        }
    next:
    int ans=bfs(points,0,grid);
    return ans;

    }
    int bfs(queue<pair<int,int>>& points,int level,vector<vector<int>>& grid){
        while(!points.empty()){
            level++;
            int points_size=points.size();
            while(points_size--){
                auto [r,l]=points.front();
                points.pop();
                for(int i=0;i<4;i++){
                    int x=r+direction[i];
                    int y=l+direction[i+1];
                    if(x>=0 && y>=0 && x<grid.size() && y<grid[0].size()){
                        if(grid[x][y]==2) continue;
                        else if(grid[x][y]==1) return level;
                        else if(grid[x][y]==0){
                            points.push({x,y});
                            grid[x][y]=2;
                        } 
                    }
                }
            }
        }
        return 0;
    }
    void dfs(vector<vector<int>>& grid,int row,int col,queue<pair<int,int>>& points){
        if(grid[row][col]==0){
            points.push({row,col});
            return;
        }
        grid[row][col]=2;
        for(int i=0;i<4;i++){
            int x=row+direction[i];
            int y=col+direction[i+1];
            if(x>=0 && y>=0 && x<grid.size() && y<grid[0].size() &&(grid[x][y]!=2)){
                dfs(grid,x,y,points);
            }
        }
    }
};

4.2Leetcode463

4.2.1题目描述

给定一个 row x col 的二维网格地图 grid ,其中:grid[i][j] = 1 表示陆地, grid[i][j] = 0 表示水域。
网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连)。格子是边长为 1 的正方形。网格为长方形,且宽度和高度均不超过 100 。计算这个岛屿的周长。

4.2.2输入输出格式

输入:grid = [[0,1,0,0],[1,1,1,0],[0,1,0,0],[1,1,0,0]]
输出:16
解释:它的周长是上面图片中的 16 个黄色的边

4.2.3代码

class Solution {
public:
    int direction[5]={1,0,-1,0,1};
    int islandPerimeter(vector<vector<int>>& grid) {
        int ans=0;
        int m=grid.size(),n=grid[0].size();
        vector<vector<bool>> flag(m,vector<bool>(n,false));
        queue<pair<int,int>> points;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]==1){
                    points.push({i,j});
                    bfs(grid,flag,points,ans);
                    goto next;
                }
            }
        }
        next:
        return ans;
    }
    void bfs(vector<vector<int>>& grid,vector<vector<bool>>& flag,queue<pair<int,int>>& points,int& ans){
        while(!points.empty()){
            auto [r,l]=points.front();
            points.pop();
            if(flag[r][l]) continue;
            flag[r][l]=true;
            int temp=4;
            for(int i=0;i<4;i++){
                int x=r+direction[i];
                int y=l+direction[i+1];
                if(x>=0 && y>=0 && x<grid.size() && y<grid[0].size()){
                    if(grid[x][y]==1){
                        temp--;
                        if(!flag[x][y]){
                            points.push({x,y});
                        }
                    }
                }
            }
            ans+=temp;
        }
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oax_knud

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值