LeetCode-搜索專題總結

搜索

深度優先搜索和廣度優先搜索是常考題型,廣泛運用於樹和圖中。

BFS

廣度優先搜索一層一層地進行遍歷,每層遍歷都以上一層遍歷的結果作爲起點,遍歷一個距離能訪問到的所有節點。需要注意的是,遍歷過的節點不能再次被遍歷。

第一層:

  • 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

廣度優先搜索一層一層遍歷,每一層得到的所有新節點,要用隊列存儲起來以備下一層遍歷的時候再遍歷。

而深度優先搜索在得到一個新節點時立即對新節點進行遍歷:從節點 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);
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章