搜索算法(五) DFS BFS 练习题

练习题

1.
力扣https://leetcode.cn/problems/surrounded-regions/这题和417类似,都是从边界朝内部搜索,417用的是DFS,这里为了练习,就用BFS。

首先从四条边界得到‘O’的坐标,加入队列。接着一层一层搜索,将所有相邻且为‘O'元素坐标加入队列,并且标记为已访问。

搜索结束后,遍历整个数组,将所有未访问的'O'标记为'X',因为所有边界可达的'O'都已被标记为访问过。

class Solution {
public:
    void solve(vector<vector<char>>& board) {
        int m = board.size();
        int n = board[0].size();
        queue<pair<int,int>> q;
        vector<vector<bool>> visit(m,vector<bool>(n,false));
        for(int i=0;i<n;i++){
            
            if(board[0][i]=='O'){
                q.push({0,i});
                visit[0][i] = true;
            }
            if(board[m-1][i]=='O'){
                q.push({m-1,i});
                visit[m-1][i] = true;
            }
        }
        for(int i=1;i<m-1;i++){
            
            if(board[i][0]=='O'){
                q.push({i,0});
                visit[i][0] = true;
            }
            if(board[i][n-1]=='O'){
                q.push({i,n-1});
                visit[i][n-1] = true;
            }
        }
        vector<int> path = {-1,0,1,0,-1};
        while(!q.empty()){
            int k = q.size();
          
            for(int i=0;i<k;i++){
               
                auto [a,b] = q.front();
                q.pop();
                for(int j=0;j<4;j++){
                    int x = a + path[j];
                    int y = b + path[j+1];
                    if(x>=0 && x<m && y>=0 && y<n && 
                        board[x][y] =='O' && visit[x][y]==false
                    ){
                        
                        q.push({x,y});
                        visit[x][y] = true;
                    }
                }
            }
        }
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(board[i][j]=='O'&& visit[i][j]==false){
                    board[i][j] = 'X';
                }
            }
        }

        


    }
};

2.力扣https://leetcode.cn/problems/binary-tree-paths/这题是一道典型的DFS,题目不难,但注意不要写成引用传递了。

不然就会像我一开始那样,string插入弹出写了半天,老是不对。(因为确定不了一个数字到底是几位char)

仔细一看,写成值传递,这样函数结束时,path上新增的内容都会自然消除。

/**
 * 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> res;
        dfs(root,"",res);
        return res;
    }

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


};

3.力扣https://leetcode.cn/problems/permutations-ii/

这题是典型的DFS,考虑到重复数字直接DFS,会产生重复的排列,有两种解决方法。

一是用set存储排列,自动去除了重复的排列。

二是跳过与前一个数字重复的数字(且前一个数字是未访问的)。

这里采用第二种方法:

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        int n = nums.size();
        vector<int> path;
        vector<vector<int>> res;
        vector<bool> visit(n,false);
        sort(nums.begin(), nums.end());
        
        dfs(nums,path,res,visit);
            
        return res;
    }

    void dfs(vector<int>& nums, vector<int> path, vector<vector<int>>& res, vector<bool> visit){
        if(path.size()==nums.size()){
            res.push_back(path);
        }else{
            int n = nums.size();
            for(int i=0;i<n;i++){
                if(visit[i]==false){
                    if(i==0 || (nums[i]!=nums[i-1]) || visit[i-1]==true){
                        path.push_back(nums[i]);
                        visit[i] = true;
                        dfs(nums,path,res,visit);
                        visit[i] = false;
                        path.pop_back();
                    }
                }
            }
        }
    }
};

4.力扣https://leetcode.cn/problems/sudoku-solver/

解数独的关键在于记录每行每列每个3x3方块的数字,然后对空格处依次遍历可能的数字。

先预处理矩阵,然后DFS,需要注意的是DFS的递归方法中,最后找到目标解法后,要在每一层递归直接return,不然会继续遍历可能的数字。

class Solution {
public:
    void solveSudoku(vector<vector<char>>& board) {
        memset(row,false,sizeof(row));
        memset(col,false,sizeof(col));
        memset(cube,false,sizeof(cube));


        for(int i=0;i<9;i++){
            for(int j=0;j<9;j++){
                if(board[i][j]=='.'){
                    path.push_back({i,j});
                }else{
                    int m = board[i][j] - '0';
                    row[i][m] = col[j][m] = cube[i/3][j/3][m] = true;
                }
            }
        }

        dfs(board,0);
    }

    bool dfs(vector<vector<char>>& board, int x){
        if(x>=path.size()){
            return true;
        }
        auto [a,b] = path[x];
        for(int i=1;i<=9;i++){
            if(!row[a][i] && !col[b][i] && !cube[a/3][b/3][i]){
                row[a][i] = col[b][i] = cube[a/3][b/3][i] = true;
                board[a][b] = '0' + i;
                if(dfs(board,x+1))
                    return true;
                row[a][i] = col[b][i] = cube[a/3][b/3][i] = false;
            }
        }
        return false;
    }

private:
    bool row[9][10];
    bool col[9][10];
    bool cube[3][3][10];
    vector<pair<int,int>> path;
};

5.力扣icon-default.png?t=N5F7https://leetcode.cn/problems/minimum-height-trees/这题挺绕的,记忆化DFS过不了所有的答案,必须要用拓朴排序。

虽然我不懂 这种解法为啥要叫拓扑排序,但看题解都这么说那就是了吧。

拓朴排序解这道题就绕开了搜索的问题。

要找高度最小的根节点,这个根节点应该出现在两个相距最远的叶子节点的中间位置。不然的话,其中一个子树的高度就会很高。

举个例子,两个叶子节点相距11,如果我选最中间的节点作为根节点,那么树高度为6.

如果随便选了叶子节点相邻的节点作为根节点,那么树高度为9。

有了这个思想之后,怎么实现代码呢?

这个题解链接里的这两张图很好地说明了方法:

 

目标是寻找距离最远的两个叶子节点的中间节点,我们无法立刻知道哪两个叶子节点相距最远。

所以每次都删除掉最外围的叶子节点(也就是入度为1的节点),删掉叶子节点之后,与它们相连的入度为2的节点就会变成新的叶子节点,这样新一轮继续删除叶子节点,更新与它们相连的节点的度数。

这里有些类似层次遍历,每一次删除最外层节点,当删除到只剩下两个或一个节点时,可以认为它们是最中间的节点,作为答案返回。 

看上图还是挺生动形象的,从相距最远的节点,一层一层剥开,最后找到中心节点。

代码:

class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
       if(n==1) return vector<int>{0};
       vector<int> degree(n,0);
       vector<vector<int>> connect(n);
       for(auto e:edges){
        connect[e[0]].emplace_back(e[1]);
        connect[e[1]].emplace_back(e[0]);
        degree[e[0]]++;
        degree[e[1]]++;
       }
        
       queue<int> q;
       for(int i=0;i<n;i++){
           if(degree[i]==1){
               q.push(i);
           }
       }
       while(n>2){
           int m = q.size();
           n -= m;
          
           for(int i=0;i<m;i++){
               int t = q.front();
               q.pop();
               for(int c:connect[t]){
                   degree[c]--;
                   if(degree[c]==1){
                       q.push(c);
                   }
               }
           }
       }
      
       vector<int> res;
       while(q.size()>0){
           res.push_back(q.front());
           q.pop();
       }
       return res;


    }




   
};

Reference:

力扣

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值