Leedcode算法专题训练(搜索)

BFS

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

第一层:

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

第二层:

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

第三层:

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

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

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

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

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

1091. Shortest Path in Binary Matrix(Medium)

Leetcode / 力扣

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

题目描述:0 表示可以经过某个位置,求解从左上角到右下角的最短路径长度。

class Solution {
    public static int shortestPathBinaryMatrix(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return -1;
        }
        // 如果起点就阻塞那就玩完啦
        if (grid[0][0]==1){
            return -1;
        }
        //定义 8个方向
        int[][] dir = {{1, -1}, {1, 0}, {1, 1}, {0,-1},{0,1},{-1,-1},{-1,0},{-1,1}};
        int m = grid.length;
        int n = grid[0].length;
        //bfs的老套路 来个队列
        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[]{0,0});   //把起点扔进去
        grid[0][0] = 1;        // 把起点标记为阻塞
        int path = 1;     // 层数
        
        while (!queue.isEmpty()){
            int size = queue.size();
            while(size-- > 0){
                int[] cur = queue.poll();
                int x  = cur[0];
                int y  = cur[1];
                
                //能放进队列里的都是为0可以走的(这一点在后面保证了)
                // 如果等于终点则返回
                if (x == m-1 && y == n-1){    //
                    return path;
                }
                
                //开始八个方向的判断
                for (int[] d : dir){
                    int x1 = x + d[0];
                    int y1 = y + d[1];
                    //这里开始过滤
                    if (x1 < 0 || x1 >= m || y1 < 0||y1>=n || grid[x1][y1]==1){
                        continue;
                    }
                    //把在数组范围内 并且为0不阻塞的放入队列中
                    queue.add(new int[]{x1,y1});
                    grid[x1][y1] = 1; // 标记
                }
            }
            path++;  //遍历完一层 这时候要 ++啦
        }
        return -1;
    }
}

2. 组成整数的最小平方数数量

279. Perfect Squares (Medium)

Leetcode / 力扣

For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9.

可以将每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。

要求解最小的平方数数量,就是求解从节点 n 到节点 0 的最短路径。

本题也可以用动态规划求解,在之后动态规划部分中会再次出现。

class Solution {
  public int numSquares(int n) {

    ArrayList<Integer> square_nums = new ArrayList<Integer>();
    for (int i = 1; i * i <= n; ++i) {
      square_nums.add(i * i);
    }

    Set<Integer> queue = new HashSet<Integer>();
    queue.add(n);

    int level = 0;
    while (queue.size() > 0) {
      level += 1;
      Set<Integer> next_queue = new HashSet<Integer>();

      for (Integer remainder : queue) {
        for (Integer square : square_nums) {
          if (remainder.equals(square)) {
            return level;
          } else if (remainder < square) {
            break;
          } else {
            next_queue.add(remainder - square);
          }
        }
      }
      queue = next_queue;
    }
    return level;
  }
}

3. 最短单词路径

127. Word Ladder (Medium)

Leetcode / 力扣

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 (!wordList.contains(endWord)) {
            return 0;
        }
        // visited修改为boolean数组
        boolean[] visited = new boolean[wordList.size()];
        int idx = wordList.indexOf(beginWord);
        if (idx != -1) {
            visited[idx] = true;
        }
        Queue<String> queue = new LinkedList<>();
        queue.add(beginWord);
        int count = 0;
        while (queue.size() > 0) {
            int size = queue.size();
            ++count;
            while (size-- > 0) {
                String start = queue.poll();
                for (int i = 0; i < wordList.size(); ++i) {
                    // 通过index判断是否已经访问
                    if (visited[i]) {
                        continue;
                    }
                    String s = wordList.get(i);
                    if (!canConvert(start, s)) {
                        continue;
                    }
                    if (s.equals(endWord)) {
                        return count + 1;
                    }
                    visited[i] = true;
                    queue.offer(s);
                }
            }
        }
        return 0;
    }

    public boolean canConvert(String s1, String s2) {
        // 因为题目说了单词长度相同,可以不考虑长度问题
        // if (s1.length() != s2.length()) return false;
        int count = 0;
        for (int i = 0; i < s1.length(); ++i) {
            if (s1.charAt(i) != s2.charAt(i)) {
                ++count;
                if (count > 1) {
                    return false;
                }
            }
        }
        return count == 1;
    }
}

 

DFS

 

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

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

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

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

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

 

1. 查找最大的连通面积

695. Max Area of Island (Medium)

Leetcode / 力扣

[[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]]
private int m, n;
private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

public int maxAreaOfIsland(int[][] grid) {
    if (grid == null || grid.length == 0) {
        return 0;
    }
    m = grid.length;
    n = grid[0].length;
    int maxArea = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            maxArea = Math.max(maxArea, dfs(grid, i, j));
        }
    }
    return maxArea;
}

private int dfs(int[][] grid, int r, int c) {
    if (r < 0 || r >= m || c < 0 || c >= n || grid[r][c] == 0) {
        return 0;
    }
    grid[r][c] = 0;
    int area = 1;
    for (int[] d : direction) {
        area += dfs(grid, r + d[0], c + d[1]);
    }
    return area;
}

2. 矩阵中的连通分量数目

200. Number of Islands (Medium)

Leetcode / 力扣

Input:
11000
11000
00100
00011

Output: 3

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

这俩个题目好典型,要结合在一起学会使用

class Solution {
    private int m, n;
    private int[][] direction={{0,1},{0,-1},{1,0},{-1,0}};

    public int numIslands(char[][] grid) {
        if(grid==null||grid.length==0){
            return 0;
        }
        m=grid.length;
        n=grid[0].length;
        int islandsNum = 0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j]!='0'){
                    dfs(grid,i,j);
                    islandsNum++;
                }
            }
        }
        return islandsNum;

    }
    private void dfs(char[][] grid,int i ,int j){
        if(i<0|| i>=m||j<0||j>=n||grid[i][j]=='0'){
            return;
        }
        grid[i][j]='0';
        for(int[] d:direction){
            dfs(grid,i+d[0],j+d[1]);
        }
    }

}

3. 好友关系的连通分量数目

547. Friend Circles (Medium)

Leetcode / 力扣

算法

给定的矩阵可以看成图的邻接矩阵。这样我们的问题可以变成无向图连通块的个数。为了方便理解,考虑如下矩阵:

M= [1 1 0 0 0 0
    1 1 0 0 0 0
    0 0 1 1 1 0
    0 0 1 1 0 0
    0 0 1 0 1 0
    0 0 0 0 0 1]

题目给出的矩阵实际上是一个邻接矩阵,因此这个题目就抽象成了已知邻接矩阵,求这个图的连通分量个数这样一个问题,就是一个模板题了,直接套模板代码即可。 

class Solution {
    private int n;
    public int findCircleNum(int[][] M) {
        n=M.length;
        int circleNum=0;
        boolean[] hasVisited=new boolean[n];
        for(int i=0;i<n;i++){
            if(!hasVisited[i]){
                dfs(M,i,hasVisited);
                circleNum++;
            }
        }
        return circleNum;
    }
    
    private void dfs(int[][] M,int i,boolean[] hasVisited){
        hasVisited[i]=true ;
        for(int k=0;k<n;k++){
            if(M[i][k]==1&&!hasVisited[k]){
                dfs(M,k,hasVisited);
            }
        }
    }
}

4. 填充封闭区域

130. Surrounded Regions (Medium)

Leetcode / 力扣

思路及解法

我们可以使用深度优先搜索实现标记操作。在下面的代码中,我们把标记过的字母 O 修改为字母 T

private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
private int m, n;

public void solve(char[][] board) {
    if (board == null || board.length == 0) {
        return;
    }

    m = board.length;
    n = board[0].length;

    for (int i = 0; i < m; i++) {
        dfs(board, i, 0);
        dfs(board, i, n - 1);
    }
    for (int i = 0; i < n; 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] == 'T') {
                board[i][j] = 'O';
            } else if (board[i][j] == 'O') {
                board[i][j] = 'X';
            }
        }
    }
}

private void dfs(char[][] board, int r, int c) {
    if (r < 0 || r >= m || c < 0 || c >= n || board[r][c] != 'O') {
        return;
    }
    board[r][c] = 'T';
    for (int[] d : direction) {
        dfs(board, r + d[0], c + d[1]);
    }
}

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

417. Pacific Atlantic Water Flow (Medium)

Leetcode / 力扣

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 {
    private int m,n;
    private int[][] matrix;
    private int[][] direction={{0,1},{0,-1},{1,0},{-1,0}};
    public List<List<Integer>> pacificAtlantic(int[][] matrix) {
        List<List<Integer>> ret =new ArrayList<>();
        if(matrix == null || matrix.length==0){
            return ret;
        }
         m=matrix.length;
         n=matrix[0].length;
         this.matrix=matrix;
         boolean[][] canReachP=new boolean[m][n];
         boolean[][] canReachA=new boolean[m][n];

         for(int i=0;i<m;i++){
             dfs(i,0,canReachP);
             dfs(i,n-1,canReachA);
        }
        for(int i=0;i<n;i++){
             dfs(0,i,canReachP);
             dfs(m-1,i,canReachA);
        }

        for(int i=0;i<m;i++){
             for(int j=0;j<n;j++){
                 if(canReachP[i][j]&&canReachA[i][j]){
                     ret.add(Arrays.asList(i,j));
                 }
            }
        }
        return ret;
    }
    private void dfs(int r,int c,boolean[][] canReach){
        if(canReach[r][c]){
            return;
        }
        canReach[r][c]=true;
        for(int[] d: direction){
            int nextR=d[0]+r;
            int nextC=d[1]+c;
            if(nextR<0||nextR>=m||nextC<0||nextC>=n||matrix[r][c]>matrix[nextR][nextC]){
                continue;
            }
            dfs(nextR,nextC,canReach);
        }
    }
}

Backtracking

Backtracking(回溯)属于 DFS。

  • 普通 DFS 主要用在 可达性问题 ,这种问题只需要执行到特点的位置然后返回即可。
  • 而 Backtracking 主要用于求解 排列组合 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。

因为 Backtracking 不是立即返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题:

  • 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;
  • 但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。

1. 数字键盘组合

17. Letter Combinations of a Phone Number (Medium)

Leetcode / 力扣

Input:Digit string "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
class Solution {
    private String[] Keys={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    public List<String> letterCombinations(String digits) {
        List<String>combinations=new ArrayList<>();
        if(digits==null||digits.length()==0){
            return combinations;
        }
        doCombination(new StringBuilder(),combinations,digits);
        return combinations;
    }
    private void doCombination(StringBuilder prefix,List<String>combinations,String digits){
        if(prefix.length()==digits.length()){
            combinations.add(prefix.toString());
            return;
        }
        int curDigits=digits.charAt(prefix.length())-'0';
        String letters=Keys[curDigits];
        for(char c: letters.toCharArray()){
            prefix.append(c);
            doCombination(prefix,combinations,digits);
            prefix.deleteCharAt(prefix.length()-1);
        }
    }
}

2. IP 地址划分

93. Restore IP Addresses(Medium)

Leetcode / 力扣

Given "25525511135",
return ["255.255.11.135", "255.255.111.35"].
class Solution {
    public List<String> restoreIpAddresses(String s) {
        List<String> ans = new ArrayList<>();
        if (s == null || s.length() == 0) {
            return ans;
        }
        backtrack(s, ans, 0, new ArrayList<>());
        return ans;
    }
    // pos-当前遍历到 s 字符串中的位置,tmp-当前存放已经确定好的 ip 段的数量
    private void backtrack(String s, List<String> ans, int pos, List<String> tmp) {
        if (tmp.size() == 4) {
            // 如果此时 pos 也刚好遍历完整个 s
            if (pos == s.length()) {
                // join 用法:例如 [[255],[255],[111],[35]] -> 255.255.111.35
                ans.add(String.join(".", tmp));
            }
            // 否则直接返回
            return;
        }
        
        // ip 地址每段最多有三个数字
        for (int i = 1; i <= 3; i++) {
            // 如果当前位置距离 s 末尾小于 3 就不用再分段了,直接跳出循环即可。
            if (pos + i > s.length()) {
                break;
            }
            
            // 将 s 的子串开始分段
            String segment = s.substring(pos, pos + i);
            int val = Integer.valueOf(segment);
            // 剪枝条件:段的起始位置不能为 0,段拆箱成 int 类型的长度不能大于 255
            if (segment.startsWith("0") && segment.length() > 1 || (i == 3 && val > 255)) {
                continue;
            }
            
            // 符合要求就加入到 tmp 中
            tmp.add(segment);
            // 继续递归遍历下一个位置
            backtrack(s, ans, pos + i, tmp);
            // 回退到上一个元素,即回溯
            tmp.remove(tmp.size() - 1);
        }
    }
}
public List<String> restoreIpAddresses(String s) {
    List<String> addresses = new ArrayList<>();
    StringBuilder tempAddress = new StringBuilder();
    doRestore(0, tempAddress, addresses, s);
    return addresses;
}

private void doRestore(int k, StringBuilder tempAddress, List<String> addresses, String s) {
    if (k == 4 || s.length() == 0) {
        if (k == 4 && s.length() == 0) {
            addresses.add(tempAddress.toString());
        }
        return;
    }
    for (int i = 0; i < s.length() && i <= 2; i++) {
        if (i != 0 && s.charAt(0) == '0') {
            break;
        }
        String part = s.substring(0, i + 1);
        if (Integer.valueOf(part) <= 255) {
            if (tempAddress.length() != 0) {
                part = "." + part;
            }
            tempAddress.append(part);
            doRestore(k + 1, tempAddress, addresses, s.substring(i + 1));
            tempAddress.delete(tempAddress.length() - part.length(), tempAddress.length());
        }
    }
}

3. 在矩阵中寻找字符串

79. Word Search (Medium)

Leetcode / 力扣

For example,
Given board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]
word = "ABCCED", -> returns true,
word = "SEE", -> returns true,
word = "ABCB", -> returns false.
class Solution {
    private int[][]  direction={{1,0},{-1,0},{0,1},{0,-1}};
    private int m;
    private int n;
    public boolean exist(char[][] board, String word) {
        if(word==null||word.length()==0){
            return true;
        }
        if(board==null||board.length==0||board[0].length==0){
            return false;
        }
        m=board.length;
        n=board[0].length;
        boolean[][] hasVisited = new boolean[m][n];
        
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(backtracking(0,i,j,hasVisited,board,word)){
                    return true;
                }
            }
        }
        return false;

    }
    private boolean backtracking(int curLen,int i, int j, boolean[][]hasVisited,char[][] board,String word){
        //记录word的长度,长度相同则认为匹配
        if(curLen==word.length()){
            return true;
        }
        if(i<0||i>=m||j<0||j>=n||board[i][j]!=word.charAt(curLen)||hasVisited[i][j]){
            return false;
        }
        hasVisited[i][j]=true;
        for(int[] d: direction){
            if(backtracking(curLen+1,i+d[0],j+d[1],hasVisited,board,word)){
                return true;
            }
        }
        hasVisited[i][j] = false;
        return false;

    }
}

4. 输出二叉树中所有从根到叶子的路径

257. Binary Tree Paths (Easy)

Leetcode / 力扣

  1
 /  \
2    3
 \
  5
["1->2->5", "1->3"]
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> tree_list=new ArrayList<>();
        if(root==null){
            return tree_list;
        }
        String bree="";
        backtrace(root ,tree_list,bree);
        return tree_list;
    }
    private void backtrace(TreeNode root ,List<String> tree_list,String bree){
        if(root==null){
            return;
        }
        bree+=root.val;
        if(root.left==null&&root.right==null){
            tree_list.add(bree);
        }
        else{
            bree+="->";
            backtrace(root.left,tree_list,bree);
            backtrace(root.right,tree_list,bree);; 
        }       
    }
}

5. 排列

46. Permutations (Medium)

Leetcode / 力扣

[1,2,3] have the following permutations:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();

        List<Integer> output = new ArrayList<Integer>();
        for (int num : nums) {
            output.add(num);
        }

        int n = nums.length;
        backtrack(n, output, res, 0);
        return res;
    }

    public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) {
        // 所有数都填完了
        if (first == n) {
            res.add(new ArrayList<Integer>(output));
        }
        for (int i = first; i < n; i++) {
            // 动态维护数组
            Collections.swap(output, first, i);
            // 继续递归填下一个数
            backtrack(n, output, res, first + 1);
            // 撤销操作
            Collections.swap(output, first, i);
        }
    }
}
class Solution {
    public List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> permutes = new ArrayList<>();
    List<Integer> permuteList = new ArrayList<>();
    boolean[] hasVisited = new boolean[nums.length];
    backtracking(permuteList, permutes, hasVisited, nums);
    return permutes;
    }

    private void backtracking(List<Integer> permuteList,        List<List<Integer>> permutes, boolean[] visited, final int[] nums) {
        if (permuteList.size() == nums.length) {
        permutes.add(new ArrayList<>(permuteList)); // 重新构造一个 List
        return;
    }
        for (int i = 0; i < visited.length; i++) {
            if (visited[i]) {
                continue;
            }
            visited[i] = true;
            permuteList.add(nums[i]);
            backtracking(permuteList, permutes, visited, nums);
            permuteList.remove(permuteList.size() - 1);
            visited[i] = false;
        }
    }
}

6. 含有相同元素求排列

47. Permutations II (Medium)

Leetcode / 力扣

[1,1,2] have the following unique permutations:
[[1,1,2], [1,2,1], [2,1,1]]

数组元素可能含有相同的元素,进行排列时就有可能出现重复的排列,要求重复的排列只返回一个。

在实现上,和 Permutations 不同的是要先排序,然后在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> permutes=new ArrayList<>();
        List<Integer> permuteList=new ArrayList<>();
        Arrays.sort(nums);
        boolean hasVisited =new boolean[nums.length];
        backtracking(permuteList,permutes,hasVisited,nums);
        return permutes;
    }
    
    private void backtracking(List<Integer> permuteList,List<List<Integer>>permutes,boolean[] visited,int[] nums){
        if(permuteList.size()==nums.length){
            permutes.add(new ArrayList<>(permuteList));
            return;
        }
        for(int i=0;i<visited.length;i++){
            if(i!=0&&nums[i]==nums[i-1]&&!visited[i-1]){
                continue;
            }
            if(visited[i]){
                continue;
            }
            visited[i]=true;
            permuteList.add(nums[i]);
            backtracking(permuteList,permutes,visited,nums);
            permuteList.remove(permuteList.size()-1);
            visited[i]=false;
        }
    }
}

7. 组合

77. Combinations (Medium)

Leetcode / 力扣

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> result =new ArrayList<>();
        if(n==0||k==0){
            return result;
        }
        LinkedList<Integer> arr=new LinkedList<>();
        backtrcak(n,k,1,arr,result);
        return result;
    }
//prenum为访问的第几个数字
    public void backtrcak(int n,int k,int prenum,LinkedList<Integer> arr,List<List<Integer>> result){
        if(arr.size()==k){
            result.add(new LinkedList<>(arr));
            return;
        }
        for(int i=prenum;i<=n;i++){
            arr.addLast(i);
            backtrcak(n,k,i+1,arr,result);
            arr.pollLast();   
        }
    }
}

8. 组合求和

39. Combination Sum (Medium)

Leetcode / 力扣

given candidate set [2, 3, 6, 7] and target 7,
A solution set is:
[[7],[2, 2, 3]]
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<Integer>arr=new ArrayList<>();
        List<List<Integer>> result=new ArrayList<>();
        if(candidates==null||candidates.length==0){
            return result;
        }
        backtrack(arr,result,candidates,target,0,0);
        return result;
    }

    private void backtrack(List<Integer>arr,List<List<Integer>> result,int[] candidates,int target,int sum,int start){
        if(target<sum)return;
        if(sum==target){
            result.add(new ArrayList<>(arr));
            return;
        }
        for(int i=start;i<candidates.length;i++){
            arr.add(candidates[i]);
            sum+=candidates[i];
            backtrack(arr,result,candidates,target,sum,i);
            sum-=candidates[i];
            arr.remove(arr.size()-1);
        }
    }
}
List<List<Integer>> lists = new ArrayList<>();

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        if (candidates == null || candidates.length == 0 || target < 0) {
            return lists;
        }

        List<Integer> list = new ArrayList<>();
        process(0, candidates, target, list);
        return lists;
    }

    private void process(int start, int[] candidates, int target, List<Integer> list) {
        //递归的终止条件
        if (target < 0) {
            return;
        }
        if (target == 0) {
            lists.add(new ArrayList<>(list));
        } else {
            for (int i = start; i < candidates.length; i++) {
                list.add(candidates[i]);
                //因为每个数字都可以使用无数次,所以递归还可以从当前元素开始
                process(i, candidates, target - candidates[i], list);
                list.remove(list.size() - 1);
            }
        }

    }

9. 含有相同元素的组合求和

40. Combination Sum II (Medium)

Leetcode / 力扣

For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8,
A solution set is:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]
class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> result =new ArrayList<>();
        if(candidates.length==0||candidates==null){
            return result;
        }
        List<Integer>arr =new ArrayList<>();
        Arrays.sort(candidates);
        backtrack(candidates,arr,result,target,0);
        return result;
    }

    private void backtrack(int[] candidates,List<Integer>arr,List<List<Integer>> result,int target,int start){
        if(target==0){
            result.add(new ArrayList<>(arr));
            return;
        }
        if(target<0){
            return;
        }
        for(int i=start;i<candidates.length;i++){
            if(i>start&&candidates[i]==candidates[i-1]){
                continue;
            }
            arr.add(candidates[i]);
            backtrack(candidates,arr,result,target-candidates[i],i+1);
            arr.remove(arr.size()-1);
        }

    }
}

10. 1-9 数字的组合求和

216. Combination Sum III (Medium)

Leetcode / 力扣

Input: k = 3, n = 9

Output:

[[1,2,6], [1,3,5], [2,3,4]]

从 1-9 数字中选出 k 个数不重复的数,使得它们的和为 n。

class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>>result=new ArrayList<>();
        List<Integer> path=new ArrayList<>();
        backtrack(k,n,1,path,result);
        return result;
    }

    private void backtrack(int k,int n, int start,List<Integer>path,List<List<Integer>>result){
        if(k==0&&n==0){
            result.add(new ArrayList<>(path));
            return;
        }
        if(k==0||n==0){
            return ;
        }
        for(int i=start;i<=9;i++){
            path.add(i);
            backtrack(k-1,n-i,i+1,path,result);
            path.remove(path.size()-1);
        }
    }
}

11. 子集

78. Subsets (Medium)

Leetcode / 力扣

找出集合的所有子集,子集不能重复,[1, 2] 和 [2, 1] 这种子集算重复

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result=new ArrayList<>();
        List<Integer> arr=new ArrayList<>();
        if(nums==null||nums.length==0){
            return result;
        }
        backtrack(0,nums,result,arr);
        return result;
    }
    private void backtrack(int i,int[] nums,List<List<Integer>> result,List<Integer> arr){
        result.add(new ArrayList<>(arr));
        for(int j=i;j<nums.length;j++){
            arr.add(nums[j]);
            backtrack(j+1,nums,result,arr);
            arr.remove(arr.size()-1);
        }
    }
}

12. 含有相同元素求子集

90. Subsets II (Medium)

Leetcode / 力扣

For example,
If nums = [1,2,2], a solution is:

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]
class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>>result=new ArrayList<>();
        List<Integer>arr=new ArrayList<>();
        if(nums.length==0||nums==null){
            return result;
        }
        Arrays.sort(nums);
        backtrack(0,nums,result,arr);
        return result;

    }
    
    private void backtrack(int i,int[] nums,List<List<Integer>> result,List<Integer> arr){
        result.add(new ArrayList<>(arr));
        for(int j=i;j<nums.length;j++){
            if(j>i&&nums[j]==nums[j-1]){
                continue;
            }
            arr.add(nums[j]);
            backtrack(j+1,nums,result,arr);
            arr.remove(arr.size()-1);
        }
    }

}

13. 分割字符串使得每个部分都是回文数

131. Palindrome Partitioning (Medium)

Leetcode / 力扣

For example, given s = "aab",
Return

[
  ["aa","b"],
  ["a","a","b"]
]
class Solution {
    public List<List<String>> partition(String s) {
        List<String>arr=new ArrayList<>();
        List<List<String>>result=new ArrayList<>();
        if(s==null||s.length()==0){
            return result;
        }
        backtrack(arr,result,s);
        return result;

    }

    private void backtrack(List<String> arr,List<List<String>>result,String s){
        if(s.length()==0){
            result.add(new ArrayList<>(arr));
            return;
        }
   
        for(int i=0;i<s.length();i++){
            if(isPalindrome(s,0,i)){
                arr.add(s.substring(0,i+1));
                backtrack(arr,result,s.substring(i+1));
                arr.remove(arr.size()-1);
            }
        }
    }

    private boolean isPalindrome(String s,int begin,int end){
        while(begin<end){
            if(s.charAt(begin++)!=s.charAt(end--)){
                return false;
            }
        }
        return true;
    }
}

14. 数独

37. Sudoku Solver (Hard)

Leetcode / 力扣

class Solution {
    //回溯算法
    public boolean solveSudoku(char[][] board) {
        return backTrace(board, 0, 0);
    }

    //注意这里的参数,row表示第几行,col表示第几列。
    private boolean backTrace(char[][] board, int row, int col) {
        //注意row是从0开始的,当row等于board.length的时候表示数独的
        //最后一行全部读遍历完了,说明数独中的值是有效的,直接返回true
        if (row == board.length)
            return true;
        //如果当前行的最后一列也遍历完了,就从下一行的第一列开始。这里的遍历
        //顺序是从第1行的第1列一直到最后一列,然后第二行的第一列一直到最后
        //一列,然后第三行的……
        if (col == board.length)
            return backTrace(board, row + 1, 0);
        //如果当前位置已经有数字了,就不能再填了,直接到这一行的下一列
        if (board[row][col] != '.')
            return backTrace(board, row, col + 1);
        //如果上面条件都不满足,我们就从1到9种选择一个合适的数字填入到数独中
        for (char i = '1'; i <= '9'; i++) {
            //判断当前位置[row,col]是否可以放数字i,如果不能放再判断下
            //一个能不能放,直到找到能放的为止,如果从1-9都不能放,就会下面
            //直接return false
            if (!isValid(board, row, col, i))
                continue;
            //如果能放数字i,就把数字i放进去
            board[row][col] = i;
            //如果成功就直接返回,不需要再尝试了
            if (backTrace(board, row, col))
                return true;
            //否则就撤销重新选择
            board[row][col] = '.';
        }
        //如果当前位置[row,col]不能放任何数字,直接返回false
        return false;
    }

    //验证当前位置[row,col]是否可以存放字符c
    private static boolean isValid(char[][] board, int row, int col, char c) {
        for (int i = 0; i < 9; i++) {
            //当前列有没有和字符c重复的
            if (board[i][col] == c)
                return false;
            //当前行有没有和字符c重复的
            if (board[row][i] == c)
                return false;
            //当前的单元格内是否有和字符c重复的
            if (board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == c)
                return false;
        }
        return true;
    }
}

15. N 皇后

51. N-Queens (Hard)

Leetcode / 力扣

    public List<List<String>> solveNQueens(int n) {
        char[][] chess = new char[n][n];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                chess[i][j] = '.';
        List<List<String>> res = new ArrayList<>();
        solve(res, chess, 0);
        return res;
    }

    private void solve(List<List<String>> res, char[][] chess, int row) {
        if (row == chess.length) {
            res.add(construct(chess));
            return;
        }
        for (int col = 0; col < chess.length; col++) {
            if (valid(chess, row, col)) {
                chess[row][col] = 'Q';
                solve(res, chess, row + 1);
                chess[row][col] = '.';
            }
        }
    }

    //row表示第几行,col表示第几列
    private boolean valid(char[][] chess, int row, int col) {
        //判断当前列有没有皇后,因为他是一行一行往下走的,
        //我们只需要检查走过的行数即可,通俗一点就是判断当前
        //坐标位置的上面有没有皇后
        for (int i = 0; i < row; i++) {
            if (chess[i][col] == 'Q') {
                return false;
            }
        }
        //判断当前坐标的右上角有没有皇后
        for (int i = row - 1, j = col + 1; i >= 0 && j < chess.length; i--, j++) {
            if (chess[i][j] == 'Q') {
                return false;
            }
        }
        //判断当前坐标的左上角有没有皇后
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (chess[i][j] == 'Q') {
                return false;
            }
        }
        return true;
    }

    //把数组转为list
    private List<String> construct(char[][] chess) {
        List<String> path = new ArrayList<>();
        for (int i = 0; i < chess.length; i++) {
            path.add(new String(chess[i]));
        }
        return path;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值