Leetcode 搜索(BFS DFS 回溯)


基礎部分

BFS

1091. 二進制矩陣中的最短路徑

中等

在一個 N × N 的方形網格中,每個單元格有兩種狀態:空(0)或者阻塞(1)。

一條從左上角到右下角、長度為 k 的暢通路徑,由滿足下述條件的單元格 C_1, C_2, ..., C_k 組成:

  • 相鄰單元格 C_iC_{i+1} 在八個方向之一上連通(此時,C_iC_{i+1} 不同且共享邊或角)
  • C_1 位於 (0, 0)(即,值為 grid[0][0]
  • C_k 位於 (N-1, N-1)(即,值為 grid[N-1][N-1]
  • 如果 C_i 位於 (r, c),則 grid[r][c] 為空(即,grid[r][c] == 0

返回這條從左上角到右下角的最短暢通路徑的長度。如果不存在這樣的路徑,返回 -1 。

示例 1:

輸入:[[0,1],[1,0]]

輸出:2

示例 2:

輸入:[[0,0,0],[1,1,0],[1,1,0]]

輸出:4

提示:

  1. 1 <= grid.length == grid[0].length <= 100
  2. grid[i][j]01
import java.util.ArrayDeque;
import java.util.Queue;

class Solution {
    public int shortestPathBinaryMatrix(int[][] grid) {
        if (grid[0][0] == 1 || grid[grid.length-1][grid[0].length-1] == 1) return -1;
        Queue<int[]> queue = new ArrayDeque<>();
        queue.add(new int[]{0,0});
        grid[0][0] = 1;
        int res = 0;
        while (!queue.isEmpty()){
            res++;
            int len = queue.size();
            while (len-- > 0){
                int[] zuobiao = queue.poll();
                int i = zuobiao[0];
                int j = zuobiao[1];
                if(i==grid.length-1 && j==grid[0].length-1) return res;
                if (i > 0 && grid[i-1][j] == 0){ //上
                    queue.add(new int[]{i-1,j});
                    grid[i-1][j] = 1;
                }
                if (i > 0 && j > 0  && grid[i-1][j-1] == 0){ //左上
                    queue.add(new int[]{i-1,j-1});
                    grid[i-1][j-1] = 1;
                }
                if (i > 0 && j < grid[0].length-1 && grid[i-1][j+1] == 0){ //右上
                    queue.add(new int[]{i-1,j+1});
                    grid[i-1][j+1] = 1;
                }
                if (i < grid.length-1 && grid[i+1][j] == 0){ //下
                    queue.add(new int[]{i+1,j});
                    grid[i+1][j] = 1;
                }
                if (i < grid.length-1 && j > 0 && grid[i+1][j-1] == 0){  //左下
                    queue.add(new int[]{i+1,j-1});
                    grid[i+1][j-1] = 1;
                }
                if (i < grid.length-1 && j < grid[0].length-1 && grid[i+1][j+1] == 0){ //右下
                    queue.add(new int[]{i+1,j+1});
                    grid[i+1][j+1] = 1;
                }
                if (j > 0 && grid[i][j-1] == 0){ //左
                    queue.add(new int[]{i,j-1});
                    grid[i][j-1] = 1;
                }
                if (j < grid[0].length-1 && grid[i][j+1] == 0){ //右
                    queue.add(new int[]{i,j+1});
                    grid[i][j+1] = 1;
                }
            }
        }
        return -1;
    }
}

279. 完全平方數

中等

給定正整數 n,找到若干個完全平方數(比如 1, 4, 9, 16, ...)使得它們的和等於 n。你需要讓組成和的完全平方數的個數最少。

示例 1:

輸入: n = 12
輸出: 3 
解釋: 12 = 4 + 4 + 4.

示例 2:

輸入: n = 13
輸出: 2
解釋: 13 = 4 + 9.
class Solution { //很意外:12 ms, 在所有 Java 提交中擊敗了94.28%的用戶
    public static int numSquares(int n) {
        List<Integer> arr = new ArrayList<>();
        Queue<Integer> queue = new ArrayDeque<>();
        for (int i = 1; ; i++){
            long a = i*i;
            if (a == n) return 1;
            if (a > n) break;
            arr.add((int) a);
            queue.add((int) a);
        }
        if (ij(arr,n)) return 2;
        int res = 0;
        while (!queue.isEmpty()){
            res++;
            int size = queue.size();
            while (size-- > 0){
                int num = queue.poll();
                if (num == n) return res;
                if (ij(arr,n-num)) return res+2;
                else {
                    for (Integer i : arr) {
                        queue.add(i+num);
                    }
                }
            }
        }
        return 1;
    }

    private static boolean ij(List<Integer> arr, int n) { 
        //雙指針查找,答案基本不會大於4,所以這樣省不少時間
        int i = 0;
        int j = arr.size() - 1;
        while (i <= j){
            int x = arr.get(i);
            int y = arr.get(j);
            if (x + y == n) return true;
            if (x + y > n) j--;
            else i++;
        }
        return false;
    }
}

127. 單詞接龍

中等

給定兩個單詞(beginWordendWord)和一個字典,找到從 beginWordendWord 的最短轉換序列的長度。轉換需遵循如下規則:

  1. 每次轉換只能改變一個字母。
  2. 轉換過程中的中間單詞必須是字典中的單詞。

說明:

  • 如果不存在這樣的轉換序列,返回 0。
  • 所有單詞具有相同的長度。
  • 所有單詞只由小寫字母組成。
  • 字典中不存在重復的單詞。
  • 你可以假設 beginWordendWord 是非空的,且二者不相同。

示例 1:

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

輸出: 5

解釋: 一個最短轉換序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
     返回它的長度 5。

示例 2:

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

輸出: 0

解釋: endWord "cog" 不在字典中,所以無法進行轉換。
class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (!wordList.contains(endWord)) return 0;
        Queue<String> queue = new ArrayDeque<>();
        queue.add(beginWord);
        int res = 1;
        while (!queue.isEmpty()){
            int size = queue.size();
            res++;
            while (size-- > 0){
                String str = queue.poll();
                if (canTran(str,endWord)) return res;
                for (int i = wordList.size()-1; i >= 0; i--) {
                    if (canTran(str,wordList.get(i))){
                        queue.add(wordList.get(i));
                        wordList.remove(i);
                    }
                }
            }
        }
        return 0;
    }

    private boolean canTran(String s1,String s2){
        boolean can = true;
        for (int i = 0; i < s1.length(); i++) {
            if (s1.charAt(i)!=s2.charAt(i)){
                if (can) can = false;
                else return false;
            }
        }
        return true;
    }
}

DFS

695. 島嶼的最大面積

中等

給定一個包含了一些 01 的非空二維數組 grid

一個 島嶼 是由一些相鄰的 1 (代表土地) 構成的組合,這里的「相鄰」要求兩個 1 必須在水平或者豎直方向上相鄰。你可以假設 grid 的四個邊緣都被 0(代表水)包圍着。

找到給定的二維數組中最大的島嶼面積。(如果沒有島嶼,則返回面積為 0 。)

示例 1:

[[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:

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

對於上面這個給定的矩陣, 返回 0

class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int row = grid.length;
        int column = grid[0].length;
        int res = 0;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < column; j++) {
                if (grid[i][j] == 1) {
                    Stack<int[]> stack = new Stack<>();
                    stack.push(new int[]{i,j});
                    int S = 0;
                    while (!stack.isEmpty()){
                        int[] point = stack.pop();
                        int x = point[0];
                        int y = point[1];
                        if (grid[x][y] == 1) {
                            S++;
                            grid[x][y] = 0;
                        }else continue;
                        if (x > 0 && grid[x-1][y] == 1) stack.push(new int[]{x-1,y});
                        if (x < row-1 && grid[x+1][y] == 1) stack.push(new int[]{x+1,y});
                        if (y > 0 && grid[x][y-1] == 1) stack.push(new int[]{x,y-1});
                        if (y < column-1 && grid[x][y+1] == 1) stack.push(new int[]{x,y+1});
                    }
                    res = res > S ? res : S;
                }
            }
        }
        return res;
    }
}

200. 島嶼數量

中等

給你一個由 '1'(陸地)和 '0'(水)組成的的二維網格,請你計算網格中島嶼的數量。

島嶼總是被水包圍,並且每座島嶼只能由水平方向或豎直方向上相鄰的陸地連接形成。

此外,你可以假設該網格的四條邊均被水包圍。

示例 1:

輸入:
[
['1','1','1','1','0'],
['1','1','0','1','0'],
['1','1','0','0','0'],
['0','0','0','0','0']
]
輸出: 1

示例 2:

輸入:
[
['1','1','0','0','0'],
['1','1','0','0','0'],
['0','0','1','0','0'],
['0','0','0','1','1']
]
輸出: 3
解釋: 每座島嶼只能由水平和/或豎直方向上相鄰的陸地連接而成。
class Solution {
    public int numIslands(char[][] grid) {
        int res = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] == '1'){
                    res++;
                    dfs(i,j,grid);
                }
            }
        }
        return res;
    }

    private void dfs(int i, int j, char[][] grid) {
        grid[i][j] = '0';
        if (i > 0 && grid[i-1][j] == '1') dfs(i-1,j,grid);
        if (i < grid.length-1 && grid[i+1][j] == '1') dfs(i+1,j,grid);
        if (j > 0 && grid[i][j-1] == '1') dfs(i,j-1,grid);
        if (j < grid[0].length-1 && grid[i][j+1] == '1') dfs(i,j+1,grid);
    }
}

547. 朋友圈

中等

班上有 N 名學生。其中有些人是朋友,有些則不是。他們的友誼具有是傳遞性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我們可以認為 A 也是 C 的朋友。所謂的朋友圈,是指所有朋友的集合。

給定一個 N * N 的矩陣 M,表示班級中學生之間的朋友關系。如果M[i][j] = 1,表示已知第 i 個和 j 個學生互為朋友關系,否則為不知道。你必須輸出所有學生中的已知的朋友圈總數。

示例 1:

輸入: 
[[1,1,0],
 [1,1,0],
 [0,0,1]]
輸出: 2 
說明:已知學生0和學生1互為朋友,他們在一個朋友圈。
第2個學生自己在一個朋友圈。所以返回2。

示例 2:

輸入: 
[[1,1,0],
 [1,1,1],
 [0,1,1]]
輸出: 1
說明:已知學生0和學生1互為朋友,學生1和學生2互為朋友,所以學生0和學生2也是朋友,所以他們三個在一個朋友圈,返回1。

注意:

  1. N 在[1,200]的范圍內。
  2. 對於所有學生,有M[i][i] = 1。
  3. 如果有M[i][j] = 1,則有M[j][i] = 1。
class Solution {
    public int findCircleNum(int[][] M) {
        int n = M.length;
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (M[i][j] == 1){
                    M[i][j] = 0;
                    M[j][i] = 0;
                    res++;
                    dfs(j,M);
                }
            }
        }
        return res;
    }

    private void dfs(int j, int[][] M) {
        for (int i = 0; i < M.length; i++) {
            if (M[i][j] == 1) {
                M[i][j] = 0;
                M[j][i] = 0;
                dfs(i,M);
            }
        }
    }
}

130. 被圍繞的區域

中等

給定一個二維的矩陣,包含 'X''O'字母 O)。

找到所有被 'X' 圍繞的區域,並將這些區域里所有的 'O''X' 填充。

示例:

X X X X
X O O X
X X O X
X O X X

運行你的函數后,矩陣變為:

X X X X
X X X X
X X X X
X O X X

解釋:

被圍繞的區間不會存在於邊界上,換句話說,任何邊界上的 'O' 都不會被填充為 'X'。 任何不在邊界上,或不與邊界上的 'O' 相連的 'O' 最終都會被填充為 'X'。如果兩個元素在水平或垂直方向相鄰,則稱它們是“相連”的。

class Solution {
    List<int[]> list = new ArrayList<>();
    boolean[][] visited;

    public void solve(char[][] board) {
        if (board.length == 0) return;
        visited = new boolean[board.length][board[0].length];
        for (int i= 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (board[i][j] == 'X' || visited[i][j]) continue;
                dfs(i,j,board);
                boolean flag = true;
                for (int[] ints : list) {
                    if (ints[0] == 0 || ints[0] == board.length-1 || ints[1] == 0 || ints[1] == board[0].length-1){
                        flag = false;
                        break;
                    }
                }
                if (flag){
                    for (int[] ints : list) {
                        board[ints[0]][ints[1]] = 'X';
                    }
                }
                list.clear();
            }
        }
    }

    private void dfs(int i, int j, char[][] board) {
        if (visited[i][j]) return;
        visited[i][j] = true;
        list.add(new int[]{i,j});
        if (i > 0 && board[i-1][j] == 'O') dfs(i-1,j,board);
        if (i < board.length-1 && board[i+1][j] == 'O') dfs(i+1,j,board);
        if (j > 0 && board[i][j-1] == 'O') dfs(i,j-1,board);
        if (j < board[0].length-1 && board[i][j+1] == 'O') dfs(i,j+1,board);
    }
}

417. 太平洋大西洋水流問題

中等

給定一個 m x n 的非負整數矩陣來表示一片大陸上各個單元格的高度。“太平洋”處於大陸的左邊界和上邊界,而“大西洋”處於大陸的右邊界和下邊界。

規定水流只能按照上、下、左、右四個方向流動,且只能從高到低或者在同等高度上流動。

請找出那些水流既可以流動到“太平洋”,又能流動到“大西洋”的陸地單元的坐標。

提示:

  1. 輸出坐標的順序不重要
  2. mn 都小於150

示例:

給定下面的 5x5 矩陣:

  太平洋 ~   ~   ~   ~   ~ 
       ~  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  *
          *   *   *   *   * 大西洋

返回:

[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (上圖中帶括號的單元).
public class Solution {
    private static int[][] dires = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    private int m, n;
    private int[][] matrix;

    public List<List<Integer>> pacificAtlantic(int[][] matrix) {
        List<List<Integer>> res = new ArrayList<>();
        m = matrix.length;
        if (m == 0) return res;
        n = matrix[0].length;
        if (n == 0) return res;
        this.matrix = matrix;
        boolean[][] canReachP = new boolean[m][n];
        boolean[][] canReachA = new boolean[m][n];
        for (int i = 0; i < n; i++) {
            dfs(0, i, canReachP);
            dfs(m - 1, i, canReachA);
        }
        for (int i = 0; i < m; i++) {
            dfs(i, 0, canReachP);
            dfs(i, n - 1, canReachA);
        }
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(canReachA[i][j] && canReachP[i][j]){
                    List<Integer> temp = new ArrayList<>();
                    temp.add(i);
                    temp.add(j);
                    res.add(temp);
                }
            }
        }
        return res;
    }

    //換一種思路,從邊界往里面走,只能走到比自己更高或者等高的地方。
    //邊界能走到的地方,就是能流入對應海洋的地方。
    private void dfs(int x, int y, boolean[][] canReach) {
        canReach[x][y] = true;
        for (int i = 0; i < 4; i++) {
            int newX = x + dires[i][0];
            int newY = y + dires[i][1];
            if (isIn(newX, newY) && matrix[x][y] <= matrix[newX][newY] && !canReach[newX][newY]) {
                dfs(newX, newY, canReach);
            }
        }
    }

    private boolean isIn(int x, int y) {
        return x >= 0 && x < m && y >= 0 && y < n;
    }
}

回溯法

17. 電話號碼的字母組合

中等

給定一個僅包含數字 2-9 的字符串,返回所有它能表示的字母組合。

給出數字到字母的映射如下(與電話按鍵相同)。注意 1 不對應任何字母。

示例:

輸入:"23"
輸出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

說明:
盡管上面的答案是按字典序排列的,但是你可以任意選擇答案輸出的順序。

class Solution {
    String[] strings = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    List<String> result = new ArrayList<>();

    public List<String> letterCombinations(String digits) {
        char[] ds = digits.toCharArray();
        if (ds.length == 0) return result;
        StringBuilder sb = new StringBuilder();
        backtrack(sb,ds,0);
        return result;
    }

    private void backtrack(StringBuilder sb, char[] ds, int index) {
        if (sb.length() == ds.length){
            result.add(sb.toString());return;
        }
        char[] chs = strings[ds[index] - '0'].toCharArray();
        for (int i = 0; i < chs.length; i++) {
            sb.append(chs[i]);
            backtrack(sb,ds,index+1);
            sb.deleteCharAt(sb.length()-1);
        }
    }
}

93. 復原IP地址

中等

給定一個只包含數字的字符串,復原它並返回所有可能的 IP 地址格式。

有效的 IP 地址正好由四個整數(每個整數位於 0 到 255 之間組成),整數之間用 '.' 分隔。

示例:

輸入: "25525511135"
輸出: ["255.255.11.135", "255.255.111.35"]
class Solution {
    List<String> result = new ArrayList<>();
    int len; //一直能復用

    public List<String> restoreIpAddresses(String s) {
        len = s.length();
        if (len <= 12) helper(s,0,"",0);
        return result;
    }

    private void helper(String ip, int index, String restore, int count) {
        if (count == 4 && index == len) result.add(restore);
        if (count >= 4) return;
        for (int i = 1; i < 4; i++) {
            if (index + i > len) break;
            String s = ip.substring(index,index+i);
            if (i > 1 && s.charAt(0) == '0' || Integer.parseInt(s) > 255)
                break; //i即s的長度
            helper(ip, index+i, restore + s + (count < 3 ? "." : "" ), count+1);
        }
    }
}

79. 單詞搜索

中等

給定一個二維網格和一個單詞,找出該單詞是否存在於網格中。

單詞必須按照字母順序,通過相鄰的單元格內的字母構成,其中“相鄰”單元格是那些水平相鄰或垂直相鄰的單元格。同一個單元格內的字母不允許被重復使用。

示例:

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

給定 word = "ABCCED", 返回 true
給定 word = "SEE", 返回 true
給定 word = "ABCB", 返回 false

提示:

  • boardword 中只包含大寫和小寫英文字母。
  • 1 <= board.length <= 200
  • 1 <= board[i].length <= 200
  • 1 <= word.length <= 10^3
class Solution {
    int m;
    int n;
    int[][] directions = {{-1,0},{1,0},{0,-1},{0,1}};
    boolean res = false;
    public boolean exist(char[][] board, String word) {
        this.m = board.length;
        this.n = board[0].length;
        if (word.length() > m * n) return false;
        boolean[][] visited = new boolean[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (word.charAt(0) == board[i][j]) backtrack(board,visited,word,i,j,0);
                if (res) break;
            }
        }
        return res;
    }

    private void backtrack(char[][] board,boolean[][] visited,String word,int i,int j,int index) {
        if (!visited[i][j] && board[i][j] == word.charAt(index)){
            if (index == word.length()-1) {
                res = true;return;
            }
            visited[i][j] = true;
            for (int k = 0; k < 4; k++) {
                int x = i + directions[k][0];
                int y = j + directions[k][1];
                if (isIn(x,y) && !visited[x][y] && board[x][y] == word.charAt(index+1)){
                    backtrack(board,visited,word,x,y,index+1);
                    if (res) return; //有滿足的了,趕緊結束遞歸
                }
            }
            visited[i][j] = false; //回溯
        }
    }

    private boolean isIn(int i,int j){
        return !(i < 0 || j < 0 || i >= m || j >= n);
    }
}

257. 二叉樹的所有路徑

簡單

給定一個二叉樹,返回所有從根節點到葉子節點的路徑。

說明: 葉子節點是指沒有子節點的節點。

示例:

輸入:

   1
 /   \
2     3
 \
  5

輸出: ["1->2->5", "1->3"]

解釋: 所有根節點到葉子節點的路徑為: 1->2->5, 1->3
class Solution {
    List<String> result = new ArrayList<>();

    public List<String> binaryTreePaths(TreeNode root) {
        if (root == null) return result;
        StringBuilder sb = new StringBuilder();
        helper(root,sb);
        return result;
    }

    private void helper(TreeNode root, StringBuilder sb) {
        if (root == null) return;
        String str = "" + root.val + "->";
        sb.append(str);
        if (root.left == null && root.right == null)
            result.add(sb.toString().substring(0,sb.length()-2));
        else {
            helper(root.left,sb);
            helper(root.right,sb);
        }
        sb.delete(sb.length()-str.length(),sb.length());
    }
}

46. 全排列

中等

給定一個 沒有重復 數字的序列,返回其所有可能的全排列。

示例:

輸入: [1,2,3]
輸出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]
class Solution {
    List<List<Integer>> result = new LinkedList<>();
    List<Integer> restore = new LinkedList<>();

    public List<List<Integer>> permute(int[] nums) {
        helper(nums);
        return result;
    }

    private void helper(int[] nums) {
        if (nums.length == restore.size()) {
            result.add(new LinkedList<>(restore)); //必須要初始化※
            //result.add(restore); //錯的
            return;
        }
        for (int num : nums) {
            if (restore.contains(num)) continue;
            restore.add(num);
            helper(nums);
            restore.remove(restore.size() - 1);
        }
    }
}

47. 全排列 II

中等

給定一個可包含重復數字的序列,返回所有不重復的全排列。

示例:

輸入: [1,1,2]
輸出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]
class Solution {
    Set<List<Integer>> result = new HashSet<>();
    List<Integer> restore = new LinkedList<>();
    Set<Integer> set = new HashSet<>();

    public List<List<Integer>> permuteUnique(int[] nums) {
        helper(nums);
        return new ArrayList<>(result);
    }

    private void helper(int[] nums) {
        if (nums.length == restore.size()) {
            result.add(new LinkedList<>(restore));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            int num = nums[i];
            if (set.contains(i)) continue;
            restore.add(num);
            set.add(i);
            helper(nums);
            restore.remove(restore.size() - 1);
            set.remove(i);
        }
    }
}

77. 組合

中等

給定兩個整數 nk,返回 1 ... n 中所有可能的 k 個數的組合。

示例:

輸入: n = 4, k = 2
輸出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> restore = new ArrayList<>();
    Set<Integer> set = new HashSet<>();

    public List<List<Integer>> combine(int n, int k) {
        helper(n,k,0);
        return result;
    }

    private void helper(int n, int k, int count) {
        if (count == k){
            result.add(new ArrayList<>(restore));
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (set.contains(i) || count > 0 && restore.get(count-1) > i) continue;
            restore.add(i);
            set.add(i);
            helper(n,k,count+1);
            restore.remove(count);
            set.remove(i);
        }
    }
}

39. 組合總和

中等

給定一個無重復元素的數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和為 target 的組合。

candidates 中的數字可以無限制重復被選取。

說明:

  • 所有數字(包括 target)都是正整數。
  • 解集不能包含重復的組合。

示例 1:

輸入:candidates = [2,3,6,7], target = 7,
所求解集為:
[
  [7],
  [2,2,3]
]

示例 2:

輸入:candidates = [2,3,5], target = 8,
所求解集為:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

提示:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每個元素都是獨一無二的。
  • 1 <= target <= 500
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> restore = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        helper(candidates,target);
        return result;
    }

    private void helper(int[] candidates, int target) {
        if (target == 0) {
            result.add(new ArrayList<>(restore));
            return;
        }
        for (int i : candidates) {
            if (target-i < 0) break;
            if (!restore.isEmpty() && i < restore.get(restore.size()-1)) continue;
            restore.add(i);
            helper(candidates, target - i);
            restore.remove(restore.size()-1);
        }
    }
}

40. 組合總和 II

中等

給定一個數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和為 target 的組合。

candidates 中的每個數字在每個組合中只能使用一次。

說明:

  • 所有數字(包括目標數)都是正整數。
  • 解集不能包含重復的組合。

示例 1:

輸入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集為:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

示例 2:

輸入: candidates = [2,5,2,1,2], target = 5,
所求解集為:
[
  [1,2,2],
  [5]
]
class Solution {
    Set<List<Integer>> result = new HashSet<>();
    List<Integer> restore = new ArrayList<>();
    Set<Integer> set = new HashSet<>();

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        helper(candidates,target);
        return new ArrayList<>(result);
    }

    private void helper(int[] candidates, int target) {
        if (target == 0) {
            result.add(new ArrayList<>(restore));
            return;
        }
        for (int i = 0; i < candidates.length; i++) {
            int num = candidates[i];
            if (target - num < 0) break;
            if (set.contains(i) || !restore.isEmpty() && num < restore.get(restore.size() - 1))continue;
            restore.add(num);
            set.add(i);
            helper(candidates, target - num);
            restore.remove(restore.size() - 1);
            set.remove(i);
        }
    }
}

216. 組合總和 III

中等

找出所有相加之和為 nk* 個數的組合。*組合中只允許含有 1 - 9 的正整數,並且每種組合中不存在重復的數字。

說明:

  • 所有數字都是正整數。
  • 解集不能包含重復的組合。

示例 1:

輸入: k = 3, n = 7
輸出: [[1,2,4]]

示例 2:

輸入: k = 3, n = 9
輸出: [[1,2,6], [1,3,5], [2,3,4]]
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> restore = new ArrayList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        helper(n,k,0);
        return result;
    }

    private void helper(int n, int k, int count) {
        if (count == k && n == 0) result.add(new ArrayList<>(restore));
        if (count >= k) return;
        int i = restore.isEmpty() ? 1 : restore.get(restore.size()-1)+1;
        for (; i <= 9; i++) {
            restore.add(i);
            helper(n-i,k,count+1);
            restore.remove(restore.size()-1);
        }
    }
}

78. 子集

中等

給定一組不含重復元素的整數數組 nums,返回該數組所有可能的子集(冪集)。

說明:解集不能包含重復的子集。

示例:

輸入: nums = [1,2,3]
輸出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> restore = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
        Arrays.sort(nums);
        helper(nums);
        return result;
    }

    private void helper(int[] nums) {
        result.add(new ArrayList<>(restore));
        if (restore.size() == nums.length) return;
        for (int i = 0; i < nums.length; i++) {
            int num = nums[i];
            if (!restore.isEmpty() && restore.get(restore.size()-1) >= num) continue;
            restore.add(num);
            helper(nums);
            restore.remove(restore.size()-1);
        }
    }
}

90. 子集 II

中等

給定一個可能包含重復元素的整數數組 nums,返回該數組所有可能的子集(冪集)。

說明:解集不能包含重復的子集。

示例:

輸入: [1,2,2]
輸出:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> restore = new ArrayList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        help(nums, 0);
        return result;
    }

    private void help(int[] nums, int index) {
        if (!result.contains(restore)) result.add(new ArrayList<>(restore));
        if (index == nums.length) return;
        for (int i = index; i < nums.length; i++) {
            restore.add(nums[i]);
            help(nums, i+1);
            restore.remove(restore.size()-1);
        }
    }
}

131. 分割回文串

中等

給定一個字符串 s,將 s 分割成一些子串,使每個子串都是回文串。

返回 s 所有可能的分割方案。

示例:

輸入: "aab"
輸出:
[
  ["aa","b"],
  ["a","a","b"]
]
class Solution {
    List<List<String>> result = new ArrayList<>();
    List<String> restore = new ArrayList<>();

    public List<List<String>> partition(String s) {
        helper(s,0,s.length());
        return result;
    }

    private void helper(String s,int index, int end) {
        if (index == end) {
            result.add(new ArrayList<>(restore));
            return;
        }
        for (int i = index+1; i <= end; i++) {
            String substring = s.substring(index, i);
            if (isPalindrome(substring)){
                restore.add(substring);
                helper(s,i,end);
                restore.remove(restore.size()-1);
            }
        }
    }

    private boolean isPalindrome(String str){
        int len = str.length();
        for (int i = 0; i < len/2; i++) {
            if (str.charAt(i) != str.charAt(len-i-1))
                return false;
        }
        return true;
    }
}

37. 解數獨

困難

編寫一個程序,通過已填充的空格來解決數獨問題。

一個數獨的解法需遵循如下規則

  1. 數字 1-9 在每一行只能出現一次。
  2. 數字 1-9 在每一列只能出現一次。
  3. 數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。

空白格用 '.' 表示。

一個數獨。

答案被標成紅色。

Note:

  • 給定的數獨序列只包含數字 1-9 和字符 '.'
  • 你可以假設給定的數獨只有唯一解。
  • 給定數獨永遠是 9x9 形式的。
class Solution {
    Set<Character>[] sets1 = new HashSet[9]; //橫向
    Set<Character>[] sets2 = new HashSet[9]; //豎向
    Set<Character>[] sets3 = new HashSet[9]; //塊

    public Solution() {
        for (int i = 0; i < 9; i++) {
            sets1[i] = new HashSet<>();
            sets2[i] = new HashSet<>();
            sets3[i] = new HashSet<>();
        }
    }

    public void solveSudoku(char[][] board) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') continue;
                sets1[i].add(board[i][j]);
                sets2[j].add(board[i][j]);
                sets3[i/3*3 + j/3].add(board[i][j]);
            }
        }
        helper(board,0,0);
    }

    private boolean helper(char[][] board,int i,int j) {
        if (j == 9){
            i++;
            j = 0;
            if (i == 9) return true; //全部填完了
        }
        if (board[i][j] == '.') {
            int block = i/3*3 + j/3;
            for (int num = 1; num <= 9; num++) {
                char ch = (char)('0'+num);
                if (sets1[i].contains(ch) || sets2[j].contains(ch) || sets3[block].contains(ch)) continue;
                board[i][j] = ch;
                sets1[i].add(ch);
                sets2[j].add(ch);
                sets3[block].add(ch);
                if (helper(board,i,j+1)) return true; //填對了,后邊就不用變回'.'了
                board[i][j] = '.';
                sets1[i].remove(ch);
                sets2[j].remove(ch);
                sets3[block].remove(ch);
            }
        }else {
            return helper(board, i, j+1);
        }
        return false; //沒找到能匹配的數
    }
}

51. N皇后

困難

n 皇后問題研究的是如何將 n 個皇后放置在 n×n 的棋盤上,並且使皇后彼此之間不能相互攻擊。

上圖為 8 皇后問題的一種解法。

給定一個整數 n,返回所有不同的 n 皇后問題的解決方案。

每一種解法包含一個明確的 n 皇后問題的棋子放置方案,該方案中 'Q''.' 分別代表了皇后和空位。

示例:

輸入: 4
輸出: [
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解釋: 4 皇后問題存在兩個不同的解法。
class Solution {
    List<List<String>> result = new ArrayList<>();
    char[][] board;

    public List<List<String>> solveNQueens(int n) {
        board = new char[n][n];
        init(board);
        helper(0,n);
        return result;
    }

    private void helper(int rowIndex, int colIndex) {
        if (rowIndex == board.length) {
            result.add(new ArrayList<>(generate(board)));
            return;
        }
        for (int i = 0; i < colIndex; i++) {
            if (canfill(rowIndex,i)){
                board[rowIndex][i] = 'Q';
                helper(rowIndex+1, colIndex);
                board[rowIndex][i] = '.';
            }
        }
    }

    private boolean canfill(int x, int y) {
        for (int i = x-1,j = y;i >=0;i--)
            if (board[i][j] == 'Q') return false;
        for (int i = x-1,j = y-1;i >=0 && j >= 0;i--,j--)
            if (board[i][j] == 'Q') return false;
        for (int i = x-1,j = y+1;i >=0 && j <= board.length-1;i--,j++)
            if (board[i][j] == 'Q') return false;
        return true;
    }

    private List<String> generate(char[][] board) {
        List<String> rows = new ArrayList<>();
        for (int i = 0; i < board.length; i++)
            rows.add(new String(board[i]));
        return rows;
    }

    private void init(char[][] board) {
        for (int i = 0; i < board.length; i++)
            for (int j = 0; j < board.length; j++)
                board[i][j] = '.';
    }
}

頻率排序

廣度:913,815,407,1036,864,199,505,847,200,994,773,863,743

深度:546,679,711,928,394,329,199,834,505,124,488,200,839,364,863,695,753,743,694

回溯:411,93,46,10,37,51,44,22


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM