Leecode Hot100 刷题笔记本-DFS系列(C++版)

97 阅读2分钟
  1. 79. 单词搜索 中等
  2. 85. 最大矩形 困难
  3. 200. 岛屿数量 中等
  4. 207. 课程表 中等
  5. 301. 删除无效的括号 困难

79. 单词搜索

Screen Shot 2023-08-07 at 8.41.11 PM.png

Screen Shot 2023-08-07 at 8.50.25 PM.png

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        rows = board.size();
        cols = board[0].size();
        for(int i = 0; i < rows; i++) {
            for(int j = 0; j < cols; j++) {
                if (dfs(board, word, i, j, 0)) return true;
            }
        }
        return false;
    }
private:
    int rows, cols;
    bool dfs(vector<vector<char>>& board, string word, int i, int j, int k) {
        if (i >= rows || i < 0 || j >= cols || j < 0 || board[i][j] != word[k]) return false;
        if (k == word.size() - 1) return true;
        board[i][j] = '\0';
        bool res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || 
                      dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);
        board[i][j] = word[k];
        return res;
    }
};

Screen Shot 2023-08-07 at 8.50.49 PM.png

85. 最大矩形

Screen Shot 2023-08-16 at 11.37.26 AM.png

200. 岛屿数量

Screen Shot 2023-08-07 at 7.58.29 PM.png

  • 遍历整个矩阵,当遇到 grid[i][j] == '1' 时,从此点开始做深度优先搜索 dfs,岛屿数 count + 1 且在深度优先搜索中删除此岛屿
  • 设目前指针指向一个岛屿中的某一点 (i, j),寻找包括此点的岛屿边界
  • 从 (i, j) 向此点的上下左右 (i+1,j),(i-1,j),(i,j+1),(i,j-1) 做深度搜索
  • 终止条件: 1: (i, j) 越过矩阵边界; 2: grid[i][j] == 0,代表此分支已越过岛屿边界
  • 搜索岛屿的同时,执行 grid[i][j] = '0',即将岛屿所有节点删除,以免之后重复搜索相同岛屿。
class Solution {
public:
    int numIslands(vector<vector<char>>& nums) {
        // 定义一个表示最终结果岛屿数量的变量
        int res = 0;
        int m = nums.size(),
            n = nums[0].size();
        // 两层for循环遍历整张表格的所有陆地
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 取出所有的陆地 
                if (nums[i][j] == '1') {
                    // 深度递归, 遍历所有的陆地
                    dfs(nums, i, j);
                    res ++;
                }
            }
        }

        return res;
    }

    void dfs(vector<vector<char>>& nums, int i, int j) {
        // 防止i和j越界, 防止超出岛屿的上下左右的范围
        if (i < 0 || j < 0 || i >= nums.size() || j >= nums[0].size() || nums[i][j] == '0')
            return;
     
        //将遍历过的陆地改为海洋,防止重复遍历
        nums[i][j] = '0'
        dfs(nums, i + 1, j);
        dfs(nums, i, j + 1);
        dfs(nums, i - 1, j);
        dfs(nums, i, j - 1);
    }
};
  • 时间复杂度:O(MN),其中 M 和 N 分别为行数和列数
  • 空间复杂度:O(MN),在最坏情况下,整个网格均为陆地,深度优先搜索的深度达到 MN

207. 课程表

Screen Shot 2023-08-29 at 7.04.38 PM.png

解法1: bfs版本

Screen Shot 2023-08-30 at 9.13.24 AM.png

  • 维护入度表, 邻接表以及队列
  • 队列存放入度为0的节点
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> indegrees(numCourses, 0); // 入度表
        vector<vector<int>> adjacency(numCourses); // 邻接表
        queue<int> q; // 队列
        // 初始化入度表以及邻接表
        // prerequisites[1] --> prerequisites[0]
        // 因此prerequisites[0]的入度 + 1,prerequissites[1]的临街顶点中加入prerequisites[0]
        for (auto &vec : prerequisites) {
            // if (!vec.empty()) {
                indegrees[vec[0]]++;
                adjacency[vec[1]].push_back(vec[0]);
            // }
        }
        // 所有入度为0的节点加入队列
        for (int i = 0; i < numCourses; ++i) {
            if (indegrees[i] == 0) {
                q.push(i);
            }
        }

        
        // bfs,也是拓扑排序的过程
        while (!q.empty()) {
            int pre = q.front(); q.pop();
            numCourses--;
            for (auto near : adjacency[pre]) {
                if (--indegrees[near] == 0) q.push(near);
            }
        }

        return numCourses == 0;

    }
};
解法1: 深度优先遍历

Screen Shot 2023-08-30 at 9.43.33 AM.png

class Solution {
public:

    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int> > adjacent(numCourses);
        vector<int> flags(numCourses);                   //用于判断节点是否已经被访问
        for(int i=0; i<prerequisites.size(); i++){
            adjacent[prerequisites[i][0]].push_back(prerequisites[i][1]);
        }     
        for(int i=0; i<numCourses; i++){
            if(!DFS(i, adjacent, flags)) return false;
        }
        return true;
    }

    bool DFS(int i, vector<vector<int> > &adjacent, vector<int> &flags){
        // 已被其他节点启动的 DFS 访问:`i == -1`
        if(flags[i]==-1) return true;
        // 有环
        if(flags[i]==1) return false;
        flags[i]=1;
        for(auto j:adjacent[i]){
            if(!DFS(j, adjacent, flags)) return false;
        }
        flags[i]=-1;
        return true;
    }

};

301. 删除无效的括号

Screen Shot 2023-08-30 at 9.52.03 AM.png

解法一:回溯 + 剪枝

Screen Shot 2023-08-30 at 10.01.24 AM.png

class Solution {
public:
    vector<string> res;
    vector<string> removeInvalidParentheses(string s) {
        int lremove = 0;
        int rremove = 0;

        for (char c : s) {
            if (c == '(') {
                lremove++;
            } else if (c == ')') {
                if (lremove == 0) {
                    rremove++;
                } else {
                    lremove--;
                }
            }
        }
        helper(s, 0, lremove, rremove);
        return res;
    }

    void helper(string str, int start, int lremove, int rremove) {
        if (lremove == 0 && rremove == 0) {
            if (isValid(str)) {
                res.push_back(str);
            }
            return;
        }
        for (int i = start; i < str.size(); i++) {
            if (i != start && str[i] == str[i - 1]) {
                continue;
            }
            // 如果剩余的字符无法满足去掉的数量要求,直接返回
            if (lremove + rremove > str.size() - i) {
                return;
            } 
            // 尝试去掉一个左括号
            if (lremove > 0 && str[i] == '(') {
                helper(str.substr(0, i) + str.substr(i + 1), i, lremove - 1, rremove);
            }
            // 尝试去掉一个右括号
            if (rremove > 0 && str[i] == ')') {
                helper(str.substr(0, i) + str.substr(i + 1), i, lremove, rremove - 1);
            }
        }
    }

    inline bool isValid(const string & str) {
        int cnt = 0;

        for (int i = 0; i < str.size(); i++) {
            if (str[i] == '(') {
                cnt++;
            } else if (str[i] == ')') {
                cnt--;
                if (cnt < 0) {
                    return false;
                }
            }
        }

        return cnt == 0;
    }
};

Screen Shot 2023-08-30 at 10.01.59 AM.png

解法二: 广度优先搜索
class Solution {
public:
    bool isValid(string str) {
        int count = 0;

        for (char c : str) {
            if (c == '(') {
                count++;
            } else if (c == ')') {
                count--;
                if (count < 0) {
                    return false;
                }
            }
        }

        return count == 0;
    }

    vector<string> removeInvalidParentheses(string s) {
        vector<string> ans;
        unordered_set<string> currSet;

        currSet.insert(s);
        while (true) {
            for (auto & str : currSet) {
                if (isValid(str))
                    ans.emplace_back(str);
            }
            if (ans.size() > 0) {
                return ans;
            }
            unordered_set<string> nextSet;
            for (auto & str : currSet) {
                for (int i = 0; i < str.size(); i++) {
                    if (i > 0 && str[i] == str[i - 1]) {
                        continue;
                    }
                    if (str[i] == '(' || str[i] == ')') {
                        nextSet.insert(str.substr(0, i) + str.substr(i + 1, str.size()));
                    }
                }
            }
            currSet = nextSet;
        }
    }
};

Screen Shot 2023-08-30 at 10.04.04 AM.png