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

posted @ 2020-07-29 10:29  鹏懿如斯  阅读(2193)  评论(0编辑  收藏  举报