6.1 算法解释

深度优先搜索和广度优先搜索是两种最常见的优先搜索方法,它们被广泛地运用在图和树等结构中进行搜索。

6.2 深度优先搜索

深度优先搜索(depth-first seach,DFS)在搜索到一个新的节点时,立即对该新节点进行遍历;因此遍历需要用先入后出的栈来实现,也可以通过与栈等价的递归来实现。对于树结构而言, 由于总是对新节点调用遍历,因此看起来是向着“深”的方向前进。
深度优先搜索也可以用来检测环路:记录每个遍历过的节点的父节点,若一个节点被再次遍 历且父节点不同,则说明有环。我们也可以用之后会讲到的拓扑排序判断是否有环路,若最后存在入度不为零的点,则说明有环。
有时我们可能会需要对已经搜索过的节点进行标记,以防止在遍历时重复搜索某个节点,这
种做法叫做状态记录或记忆化(memoization)。

6.3 岛屿的最大面积

695. 岛屿的最大面积Mid

image.png
给你一个大小为 m x n 的二进制矩阵 grid 。
岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
岛屿的面积是岛上值为 1 的单元格的数目。
计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。
示例 1:
输入:grid = [[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:
输入:grid = [[0,0,0,0,0,0,0,0]] 输出:0

  1. int dx[4] = {1, -1, 0, 0};
  2. int dy[4] = {0, 0, 1, -1};
  3. class Solution {
  4. public:
  5. int maxAreaOfIsland(vector<vector<int>>& grid) {
  6. int x = grid.size();
  7. int y = x == 0 ? 0 : grid[0].size();
  8. vector<vector<bool>> visited(x, vector<bool>(y, false));
  9. stack<pair<int, int>> st;
  10. int sum = 0, res = 0;
  11. for (int i = 0; i < x; i++) {
  12. for (int j = 0; j < y; j++) {
  13. visited[i][j] = true;
  14. if (grid[i][j] == 1) {
  15. sum++;
  16. st.push(make_pair(i, j));
  17. }
  18. while (!st.empty()) {
  19. pair<int, int> tmp = st.top();
  20. st.pop();
  21. for (int k = 0; k < 4; k++) {
  22. int nx = tmp.first + dx[k];
  23. int ny = tmp.second + dy[k];
  24. if (nx >= 0 && nx < x && ny >= 0 && ny < y
  25. && grid[nx][ny] == 1 && false == visited[nx][ny]) {
  26. visited[nx][ny] = true;
  27. sum++;
  28. st.push(make_pair(nx, ny));
  29. }
  30. }
  31. }
  32. res = max(res, sum);
  33. sum = 0;
  34. }
  35. }
  36. return res;
  37. }
  38. };
  1. class Solution {
  2. public:
  3. int dx[4] = {1, -1, 0, 0};
  4. int dy[4] = {0, 0, 1, -1};
  5. int maxAreaOfIsland(vector<vector<int>>& grid) {
  6. int x = grid.size();
  7. int y = x == 0 ? 0 : grid[0].size();
  8. int res = 0;
  9. vector<vector<bool>> visited(x, vector<bool>(y, false));
  10. for (int i = 0; i < x; i++) {
  11. for (int j = 0; j < y; j++) {
  12. res = max(res, dfs(grid, visited, i, j));
  13. }
  14. }
  15. return res;
  16. }
  17. int dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
  18. if (grid[x][y] == 0)
  19. return 0;
  20. visited[x][y] = true;
  21. int res = 1;
  22. for (int i = 0; i < 4; i++) {
  23. int nx = x + dx[i];
  24. int ny = y + dy[i];
  25. if (nx >= 0 && nx < grid.size() && ny >= 0 && ny < grid[0].size() && visited[nx][ny] == false) {
  26. res += dfs(grid, visited, nx, ny);
  27. }
  28. }
  29. return res;
  30. }
  31. };
  1. class Solution {
  2. public:
  3. int dx[4] = {1, -1, 0, 0};
  4. int dy[4] = {0, 0, 1, -1};
  5. int maxAreaOfIsland(vector<vector<int>>& grid) {
  6. int x = grid.size();
  7. int y = x == 0 ? 0 : grid[0].size();
  8. int res = 0;
  9. int sum = 0;
  10. vector<vector<bool>> visited(x, vector<bool>(y, false));
  11. queue<pair<int, int>> q;
  12. for (int i = 0; i < x; i++) {
  13. for (int j = 0; j < y; j++) {
  14. visited[i][j] = true;
  15. if (grid[i][j] == 1) {
  16. sum++;
  17. q.push(make_pair(i, j));
  18. }
  19. while (!q.empty()) {
  20. pair<int, int> tmp = q.front();
  21. q.pop();
  22. for (int k = 0; k < 4; k++) {
  23. int nx = tmp.first + dx[k];
  24. int ny = tmp.second + dy[k];
  25. if (nx >= 0 && nx < x && ny >= 0 && ny < y && grid[nx][ny] == 1 && visited[nx][ny] == false) {
  26. visited[nx][ny] = true;
  27. sum++;
  28. q.push(make_pair(nx, ny));
  29. }
  30. }
  31. }
  32. res = max(res, sum);
  33. sum = 0;
  34. }
  35. }
  36. return res;
  37. }
  38. };

934. 最短的桥

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

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

class Solution {
    public:
        vector<int> direction{-1, 0, 1, 0, -1};
    // 主函数
    int shortestBridge(vector<vector<int>> &grid)
    {
        int m = grid.size(), n = grid[0].size();
        queue<pair<int, int>> points; // dfs寻找第一个岛屿,并把1全部赋值为2
        bool flipped = false;
        for (int i = 0; i < m; ++i)
        {
            if (flipped)
                break;
            for (int j = 0; j < n; ++j)
            {
                if (grid[i][j] == 1)
                {
                    dfs(points, grid, m, n, i, j);
                    flipped = true;
                    break;
                }
            }
        }
        // bfs寻找第二个岛屿,并把过程中经过的0赋值为2
        int x, y;
        int level = 0;
        while (!points.empty())
        {
            ++level;
            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 (grid[x][y] == 2)
                        {
                            continue;
                        }
                        if (grid[x][y] == 1)
                        {
                            return level;
                        }
                        points.push({x, y});
                        grid[x][y] = 2;
                    }
                }
            }
        }
        return 0;
    }
    // 辅函数
    void dfs(queue<pair<int, int>> &points, vector<vector<int>> &grid, int m, int n, int i, int j)
    {
        if (i < 0 || j < 0 || i == m || j == n || grid[i][j] == 2)
        {
            return;
        }
        if (grid[i][j] == 0)
        {
            points.push({i, j});
            return;
        }
        grid[i][j] = 2;
        dfs(points, grid, m, n, i - 1, j);
        dfs(points, grid, m, n, i + 1, j);
        dfs(points, grid, m, n, i, j - 1);
        dfs(points, grid, m, n, i, j + 1);
    }
};

46. 全排列

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

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

class Solution {
public:
    vector<vector<int>> res;
    vector<int> item;

    vector<vector<int>> permute(vector<int>& nums) {
        if(nums.size() < 1) return res;
        vector<bool> visited(nums.size(),false);
        dfs(visited,nums);
        return res;
    }

    void dfs(vector<bool>& visited,vector<int>& nums) {
        if(item.size() == nums.size()) {
            res.push_back(item);
            return;
        }

        for(int i = 0 ; i < nums.size() ; i++) {
            if(visited[i])
                continue;
            item.push_back(nums[i]);
            visited[i] = true;
            dfs(visited,nums);
            item.pop_back();
            visited[i] = false;
        }
    }
};

17. 电话号码的字母组合

难度中等1863
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
image.png

示例 1:
输入:digits = “23” 输出:[“ad”,”ae”,”af”,”bd”,”be”,”bf”,”cd”,”ce”,”cf”]
示例 2:
输入:digits = “” 输出:[]
示例 3:
输入:digits = “2” 输出:[“a”,”b”,”c”]

class Solution {
public:
    vector<string> board = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    vector<string> res;
    string item;

    vector<string> letterCombinations(string digits) {
        if (digits.length() == 0)
            return res;

        dfs(digits, 0);
        return res;
    }

    void dfs(const string& digits, int index)
    {
        // 1、截止条件
        if (index == digits.size())
        {
            res.push_back(item);
            return;
        }

        int num = digits[index] - '0';
        // 2、遍历候选节点
        for (int i = 0; i < board[num].length(); i++)
        {
            item.push_back(board[num][i]);
            dfs(digits, index + 1);
            item.pop_back();
        }

    }
};

22. 括号生成

难度中等2588
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:
输入:n = 3 输出:[“((()))”,”(()())”,”(())()”,”()(())”,”()()()”]
示例 2:
输入:n = 1 输出:[“()”]

class Solution {
public:
    vector<string> res;
    string item;
    vector<string> generateParenthesis(int n) {   
        backtrace(n, n);
        return res;
    }

    void backtrace(int l, int r) {
        if (l == 0 && r == 0) {
            res.push_back(item);
            return;
        }

        if (l > r) {
            return;
        }

        if (l > 0) {
            item.push_back('(');
            backtrace(l - 1, r);
            item.pop_back();
        }

        if (r > 0) {
            item.push_back(')');
            backtrace(l , r - 1);
            item.pop_back();
        }
    }
};

77. 组合

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

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

class Solution {
public:
    vector<vector<int>> res;
    vector<int> item;
    vector<vector<int>> combine(int n, int k) {
        backtrace(1, n, k);
        return res;
    }

    void backtrace(int index, int n, int k) {
        if (item.size() == k) {
            res.push_back(item);
            return;
        }

        for (int i = index; i <= n; i++) {
            item.push_back(i);
            backtrace(i + 1, n, k);
            item.pop_back();
        }
    }
};
class Solution {
public:
    vector<vector<int>> res;
    vector<int> item;
    vector<vector<int>> combine(int n, int k) {
        backtracking(n, k, 1);
        return res;
    }

    void backtracking(int n, int k, int startIndex) {
        if (item.size() == k) {
            res.push_back(item);
            return;
        }
        //剪枝
        for (int i = startIndex; i <= n - k + 1 + item.size(); i++) {
            item.push_back(i);
            backtracking(n, k, i + 1);
            item.pop_back();
        }
    }
};

79. 单词搜索

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

示例 1:
输入:board = [[“A”,”B”,”C”,”E”],[“S”,”F”,”C”,”S”],[“A”,”D”,”E”,”E”]], word = “ABCCED” 输出:true
image.pngimage.png
示例 2:
输入:board = [[“A”,”B”,”C”,”E”],[“S”,”F”,”C”,”S”],[“A”,”D”,”E”,”E”]], word = “SEE” 输出:true
示例 3:
输入:board = [[“A”,”B”,”C”,”E”],[“S”,”F”,”C”,”S”],[“A”,”D”,”E”,”E”]], word = “ABCB” 输出:false

class Solution {
public:
    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, -1, 1};
    bool exist(vector<vector<char>>& board, string word) {
        int m = board.size();
        int n = board[0].size();
        vector<vector<bool>> visited(m, vector<bool>(n, false));

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (word[0] == board[i][j]) {
                    if (dfs(board, visited, word, i, j, 0))
                        return true;
                }
            }
        }

        return false;
    }

    bool dfs(vector<vector<char>>& board, vector<vector<bool>>& visited, string& word, int row, int col, int index) {
        if (index == word.length())
            return true;

        if (row < 0 || col < 0 || row >= board.size() || col >= board[0].size())
            return false;

        if (visited[row][col])
            return false;

        if (board[row][col] != word[index])
            return false;

        visited[row][col] = true;
        for (int i = 0; i < 4; i++) {
            int nx = dx[i] + row;
            int ny = dy[i] + col;
            if (dfs(board, visited, word, nx, ny, index + 1))
                return true;
        }
        visited[row][col] = false;
        return false;
    }
};

39. 组合总和

难度中等1927
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:
输入:candidates = [2,3,6,7], target = 7 输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。
示例 2:
输入: candidates = [2,3,5], target = 8 输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2], target = 1 输出: []

class Solution {
public:
    vector<vector<int>> res;
    vector<int> item;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backtracking(candidates, target, 0, 0);
        return res;
    }

    void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {
        if (sum > target) return;
        if (sum == target) {
            res.push_back(item);
            return;
        }

        for (int i = startIndex; i < candidates.size(); i++) {
            item.push_back(candidates[i]);
            sum += candidates[i];
            backtracking(candidates, target, sum, i);
            sum -= candidates[i];
            item.pop_back();
        }
    }
};

image.png