LeetCode-搜索专题总结

搜索

深度优先搜索和广度优先搜索是常考题型,广泛运用于树和图中。

BFS

LeetCode-搜索专题总结_第1张图片

广度优先搜索一层一层地进行遍历,每层遍历都以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。

第一层:

  • 0 -> {6,2,1,5}

第二层:

  • 6 -> {4}
  • 2 -> {}
  • 1 -> {}
  • 5 -> {3}

第三层:

  • 4 -> {}
  • 3 -> {}

每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 最优解 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径。

在程序实现 BFS 时需要考虑以下问题:

  • 队列:用来存储每一轮遍历得到的节点;
  • 标记:对于遍历过的节点,应该将它标记,防止重复遍历。

计算在网格中从原点到特定点的最短路径长度

[[1,1,0,1],
 [1,0,1,0],
 [1,1,1,1],
 [1,0,1,1]]

1 表示可以经过某个位置,求解从 (0, 0) 位置到 (tr, tc) 位置的最短路径长度。

public int minPathLength(int[][] grids, int tr, int tc) {
    final int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    final int m = grids.length, n = grids[0].length;
    Queue<Pair<Integer, Integer>> queue = new LinkedList<>();
    queue.add(new Pair<>(0, 0));
    int pathLength = 0;
    while (!queue.isEmpty()) {
        int size = queue.size();
        pathLength++;
        while (size-- > 0) {
            Pair<Integer, Integer> cur = queue.poll();
            int cr = cur.getKey(), cc = cur.getValue();
            grids[cr][cc] = 0; // 标记
            for (int[] d : direction) {
                int nr = cr + d[0], nc = cc + d[1];
                if (nr < 0 || nr >= m || nc < 0 || nc >= n || grids[nr][nc] == 0) {
                    continue;
                }
                if (nr == tr && nc == tc) {
                    return pathLength;
                }
                queue.add(new Pair<>(nr, nc));
            }
        }
    }
    return -1;
}

最短单词路径

127. Word Ladder (Medium)

Input:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

Output: 5

Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Input:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

Output: 0

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.

题目描述:找出一条从 beginWord 到 endWord 的最短路径,每次移动规定为改变一个字符,并且改变之后的字符串必须在 wordList 中。

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (null == beginWord || 0 == beginWord.length() || null == endWord || 0 == endWord.length()) {
            return 0;
        }
        wordList.add(beginWord);
        int n = wordList.size();
        int start = n - 1;
        int end = 0;
        for (String str : wordList) {
            if (!endWord.equals(str)) {
                end++;
            } else {
                break;
            }
        }
        if (n == end) {
            return 0;
        }
        List<Integer>[] graph = getList(wordList);
        return getPath(graph, start, end);
    }
    public int getPath(List<Integer>[] graph, int start, int end) {
        int n = graph.length;
        Queue<Integer> queue = new LinkedList<>();
        boolean[] marked = new boolean[n];
        marked[start] = true;
        queue.add(start);
        int path = 1;
        while (!queue.isEmpty()) {
            int size = queue.size();
            path++;
            while (size-- > 0) {
                int position = queue.poll();
                for (int next : graph[position]) {
                    if (next == end) {
                        return path;
                    }
                    if (marked[next]) {
                        continue;
                    }
                    marked[next] = true;
                    queue.add(next);
                }
            }
        }
        return 0;
    }
    public List<Integer>[] getList(List<String> wordList) {
        int n = wordList.size();
        List<Integer>[] graph = new List[n];
        for (int i = 0; i < n; i++) {
            graph[i] = new ArrayList<>();
            for (int j = 0; j < n; j++) {
                String temp = wordList.get(j);
                if (connect(wordList.get(i), temp)) {
                    graph[i].add(j);
                }
            }
        }
        return graph;
    }
    public boolean connect(String s1, String s2) {
        int difference = 0;
        for (int i = 0; i < s1.length(); i++) {
            if (s1.charAt(i) != s2.charAt(i)) {
                difference++;
            }
        }
        return difference == 1;
    }
}

DFS

LeetCode-搜索专题总结_第2张图片

广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。

而深度优先搜索在得到一个新节点时立即对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。

从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 可达性 问题。

在程序实现 DFS 时需要考虑以下问题:

  • 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
  • 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。

查找最大的连通面积

695. Max Area of Island (Easy)

[[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]]
class Solution {
    int[][] dir = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    int cur = 0;
    public int maxAreaOfIsland(int[][] grid) {
        if (null == grid || 0 == grid.length) {
            return 0;
        }
        int m = grid.length;
        int n = grid[0].length;
        int max = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (1 == grid[i][j]) {
                    cur = 0;
                    dfs(grid, i, j);
                    max = Math.max(max, cur);
                }
            }
        }
        return max;
    }
    public void dfs(int[][] grid, int x, int y) {
        int m = grid.length;
        int n = grid[0].length;
        if (grid[x][y] == 0) {
            return ;
        } else if (grid[x][y] == 1) {
            grid[x][y] = 0;
            cur++;
        }
        for (int i = 0; i < 4; i++) {
            int xx = x + dir[i][0];
            int yy = y + dir[i][1];
            if (xx >= 0 && xx < m && yy >= 0 && yy < n) {
                dfs(grid, xx, yy);
            }
        }
    }
}

矩阵中的连通分量数目

200. Number of Islands (Medium)

Input:
11000
11000
00100
00011

Output: 3

可以将矩阵表示看成一张有向图。

class Solution {
    int[][] dir = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    public int numIslands(char[][] grid) {
        if (null == grid || 0 == grid.length) {
            return 0;
        }
        int m = grid.length;
        int n = grid[0].length;
        int islands = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == '1') {
                    dfs(grid, i, j);
                    islands++;
                }
            }
        }
        return islands;
    }
    public void dfs(char[][] grid, int x, int y) {
        int m = grid.length;
        int n = grid[0].length;
        if ('0' == grid[x][y]) {
            return ;
        } else if ('1' == grid[x][y]){
            grid[x][y] = '0'; 
        }
        for (int i = 0; i < 4; i++) {
            int xx = x + dir[i][0];
            int yy = y + dir[i][1];
            if (xx >= 0 && xx < m && yy >= 0 && yy < n) {
                dfs(grid, xx, yy);
            }
        }
    }
}

好友关系的连通分量数目

547. Friend Circles (Medium)

Input:
[[1,1,0],
 [1,1,0],
 [0,0,1]]

Output: 2

Explanation:The 0th and 1st students are direct friends, so they are in a friend circle.
The 2nd student himself is in a friend circle. So return 2.

题目描述:好友关系可以看成是一个无向图,例如第 0 个人与第 1 个人是好友,那么 M[0][1] 和 M[1][0] 的值都为 1。

class Solution {
    public int findCircleNum(int[][] M) {
        if (null == M || 0 == M.length) {
            return 0;
        }
        int m = M.length;
        int circles = 0;
        boolean[] visit = new boolean[m];
        for (int i = 0; i < m; i++) {
            if (!visit[i]) {
                dfs(M, i, visit);
                circles++;
            }
        }
        return circles;
    }
    public void dfs(int[][] M, int i, boolean[] visit) {
        visit[i] = true;
        int m = M.length;
        for (int k = 0; k < m; k++) {
            if (!visit[k] && 1 == M[i][k]) {
                dfs(M, k, visit);
            }
        }
    }
}

填充封闭区域

130. Surrounded Regions (Medium)

For example,
X X X X
X O O X
X X O X
X O X X

After running your function, the board should be:
X X X X
X X X X
X X X X
X O X X

题目描述:使被 ‘X’ 包围的 ‘O’ 转换为 ‘X’。

先将最外侧一层搜索一遍,再处理里层。

class Solution {
    int[][] dir = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    public void solve(char[][] board) {
        if (null == board || 0 == board.length) {
            return ;
        }
        int m = board.length;
        int n = board[0].length;
        for (int i = 0; i < m; i++) {
            dfs(board, i, 0);
            dfs(board, i, n - 1);
        }
        for (int i = 1; i < n - 1; i++) {
            dfs(board, 0, i);
            dfs(board, m - 1, i);
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == 'A') {
                    board[i][j] = 'O';
                } else if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                }
            }
        }
    }
    public void dfs(char[][] board, int x, int y) {
        int m = board.length;
        int n = board[0].length;
        if (board[x][y] != 'O') {
            return ;
        }
        board[x][y] = 'A';
        for (int i = 0; i < 4; i++) {
            int xx = x + dir[i][0];
            int yy = y + dir[i][1];
            if (xx >= 0 && xx < m && yy >= 0 && yy < n) {
                dfs(board, xx, yy);
            }
        }
    }
}

能到达的太平洋和大西洋的区域

417. Pacific Atlantic Water Flow (Medium)

Given the following 5x5 matrix:

  Pacific ~   ~   ~   ~   ~
       ~  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  *
          *   *   *   *   * Atlantic

Return:
[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix).

左边和上边是太平洋,右边和下边是大西洋,内部的数字代表海拔,海拔高的地方的水能够流到低的地方,求解水能够流到太平洋和大西洋的所有位置。

class Solution {
    int[][] dir = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    boolean[][] Pacific;
    boolean[][] Atlantic;
    List<int[]> list;
    public List<int[]> pacificAtlantic(int[][] matrix) {
        list = new ArrayList<>();
        if (null == matrix || 0 == matrix.length) {
            return list;
        }
        int m = matrix.length;
        int n = matrix[0].length;
        Pacific = new boolean[m][n];
        Atlantic = new boolean[m][n];
        for (int i = 0; i < n; i++) {
            dfs(matrix, 0, i, Pacific);
        }
        for (int i = 1; i < m; i++) {
            dfs(matrix, i, 0, Pacific);
        }
        for (int i = 0; i < m; i++) {
            dfs(matrix, i, n - 1, Atlantic);
        }
        for (int i = 0; i < n - 1; i++) {
            dfs(matrix, m - 1, i, Atlantic);
        }
        for (int i = 0; i < m ; i++) {
            for (int j = 0; j < n; j++) {
                if (Pacific[i][j] && Atlantic[i][j]) {
                    list.add(new int[]{i, j});
                }
            }
        }
        return list;
    }
    public void dfs(int[][] matrix, int x, int y, boolean[][] vis) {
        if (vis[x][y]) {
            return ;
        }
        vis[x][y] = true;
        int m = matrix.length;
        int n = matrix[0].length;
        for (int i = 0; i < 4; i++) {
            int xx = x + dir[i][0];
            int yy = y + dir[i][1];
            if (xx >= 0 && xx < m && yy >= 0 && yy < n && matrix[xx][yy] >= matrix[x][y]) {
                dfs(matrix, xx, yy, vis);
            }
        }
    }
}

你可能感兴趣的:(coding)