回溯算法习题集

易错点

  1. 遇到元素重复的题,一定要记住对数组排序Arrays.sort(nums)
  2. 遇到求和target的题目,要在判断条件中加入if(trackSum > target)
  3. 当遇到多个集合的问题时,只需要递归加嵌套循环即可,电话号码以及N皇后均是此类问题。
  4. 括号生成问题关键:[1,i]中左括号数量 >= 右括号数量

1. leetcode-77:组合

题目:给定两个整数 n 和 k,返回范围[1, n]中所有可能的k个数的组合。

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    public List<List<Integer>> combine(int n, int k) {
        backtrack(n,1,k);
        return res;
    }
    void backtrack(int n, int start, int k) {
        if(track.size() == k) {
            res.add(new LinkedList<>(track));
            return;
        }
        for(int i=start; i<=n-(k-track.size())+1; i++) {
            track.add(i);
            backtrack(n,i+1,k);
            track.removeLast();
        }
    }
}

2. leetcode-216:组合III

题目:找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:(1)、只使用数字1到9 (2)、每个数字最多使用一次

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    int trackSum = 0;    
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtrack(k,1,n);
        return res;
    }
    void backtrack(int k, int start, int n) {
        if(trackSum > n) {
            return;
        }
        if(track.size() == k && trackSum == n) {
            res.add(new LinkedList<>(track));
            return;
        }
        for(int i=start; i<=9-(k-track.size())+1; i++) {
            trackSum += i;
            track.add(i);
            backtrack(k,i+1,n);
            track.removeLast();
            trackSum -= i;
        }
    }
}

3. leetcode-39:组合之和

题目:给你一个无重复元素的整数数组 candidates 和一个目标整数target ,找出 candidates中可以使数字和为目标数target的所有不同组合,并以列表形式返回。candidates中的同一个数字可以无限制重复被选取。如果至少一个数字的被选数量不同,则两种组合是不同的。

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    int trackSum = 0;
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backtrack(candidates,target,0);
        return res;
    }
    void backtrack(int[] candidates, int target, int start) {
        if(trackSum == target) {
            res.add(new LinkedList<>(track));
            return;
        }
        if(trackSum > target) {
            return;
        }
        for(int i=start; i<candidates.length; i++) {
            trackSum += candidates[i];
            track.add(candidates[i]);
            backtrack(candidates,target,i);
            track.removeLast();
            trackSum -= candidates[i];
        }
    }
}

4. leetcode-40:组合II

题目:给定一个候选人编号的集合candidates和一个目标数 target ,找出candidates中所有可以使数字和为target的组合。candidates中的每个数字在每个组合中只能使用一次。

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    int trackSum = 0;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtrack(candidates,target,0);
        return res;
    }
    void backtrack(int[] candidates, int target, int start) {
        if(trackSum == target) {
            res.add(new LinkedList<>(track));
            return;
        }
        if(trackSum > target) {
            return;
        }
        for(int i=start; i<candidates.length; i++) {
            if(i > start && candidates[i] == candidates[i-1]) {
                continue;
            }
            trackSum += candidates[i];
            track.add(candidates[i]);
            backtrack(candidates,target,i+1);
            trackSum -= candidates[i];
            track.removeLast();
        }
    }
}

5. leetcode-78:子集

题目:给你一个整数数组nums,数组中的元素互不相同。返回该数组所有可能的子集(幂集)。

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    public List<List<Integer>> subsets(int[] nums) {
        backtrack(nums,0);
        return res;
    }
    void backtrack(int[] nums, int start) {
        res.add(new LinkedList<>(track));
        for(int i=start; i<nums.length; i++) {
            track.add(nums[i]);
            backtrack(nums,i+1);
            track.removeLast();
        }
    }
}

6. leetcode-90:子集II

题目:给你一个整数数组nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集不能包含重复的子集

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        backtrack(nums,0);
        return res;
    }
    void backtrack(int[] nums, int start) {
        res.add(new LinkedList<>(track));
        for(int i=start; i<nums.length; i++) {
            if(i > start && nums[i] == nums[i-1]) {
                continue;
            }
            track.add(nums[i]);
            backtrack(nums,i+1);
            track.removeLast();
        }
    }
}

7. leetcode-46:全排列

题目:给定一个不含重复数字的数组nums,返回其所有可能的全排列。

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    boolean[] used;
    public List<List<Integer>> permute(int[] nums) {
        used = new boolean[nums.length];
        backtrack(nums);
        return res;
    }
    void backtrack(int[] nums) {
        if(track.size() == nums.length) {
            res.add(new LinkedList<>(track));
            return;
        }
        for(int i=0; i<nums.length; i++) {
            if(used[i]) {
                continue;
            }
            used[i] = true;
            track.add(nums[i]);
            backtrack(nums);
            track.removeLast();
            used[i] = false;
        }
    }
}

8. leetcode-47:全排列II

题目:给定一个可包含重复数字的序列nums,按任意顺序返回所有不重复的全排列。

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    boolean[] used;
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        used = new boolean[nums.length];
        backtrack(nums);
        return res;
    }
    void backtrack(int[] nums) {
        if(track.size() == nums.length) {
            res.add(new LinkedList<>(track));
            return;
        }
        for(int i=0; i<nums.length; i++) {
            if(used[i]) {
                continue;
            }
            if(i > 0 && nums[i] == nums[i-1] && !used[i-1]) {
                continue;
            }
            used[i] = true;
            track.add(nums[i]);
            backtrack(nums);
            track.removeLast();
            used[i] = false;
        }
    }
}

9. leetcode-17:电话号码的字母组合

题目:给定一个仅包含数字2-9的字符串,返回所有它能表示的字母组合。

class Solution {
    List<String> res = new LinkedList<>();
    String[] mapping = new String[]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    public List<String> letterCombinations(String digits) {
        if(digits.isEmpty()) {
            return res;
        }
        backtrack(digits,0,new StringBuilder());
        return res;
    }
    void backtrack(String digits, int start, StringBuilder sb) {
        if(sb.length() == digits.length()) {
            res.add(sb.toString());
            return;
        }
        for(int i=start; i<digits.length(); i++) {
            int digit = digits.charAt(i) - '0';
            for(char c : mapping[digit].toCharArray()) {
                sb.append(c);
                backtrack(digits,i+1,sb);
                sb.deleteCharAt(sb.length()-1);
            }
        }
    }
}

另一种思路:

class Solution {
    List<String> res = new LinkedList<>();//结果集
    String[] mapping = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    StringBuilder sb = new StringBuilder();//路径集
    public List<String> letterCombinations(String digits) {
        if(digits.isEmpty()) {
            return res;
        }
        backtrack(digits,0);
        return res;
    }
    void backtrack(String digits, int row) {
        if(sb.length() == digits.length()) {
            res.add(sb.toString());
            return;
        }
        //第一行循环,第二行循环...
        String str = mapping[digits.charAt(row) - '0'];
        for(int i=0; i<str.length(); i++) {
            sb.append(str.charAt(i));
            backtrack(digits,row+1);
            sb.deleteCharAt(sb.length()-1);
        }
    }
}

10. leetcode-491:递增子序列

题目:给你一个整数数组nums,找出并返回所有该数组中不同的递增子序列,递增子序列中至少有两个元素 。你可以按任意顺序返回答案。数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();   
    public List<List<Integer>> findSubsequences(int[] nums) {
        backtack(nums,0);
        return res;
    }
    void backtack(int[] nums, int start) {
        Set<Integer> used = new HashSet<>();
        if(track.size() > 1) {
            res.add(new LinkedList<>(track));
        }
        for(int i=start; i<nums.length; i++) {
            //相当于排序+nums[i]==nums[i-1]
            if(used.contains(nums[i])){
                continue;
            }
            //递增判断逻辑
            if(!track.isEmpty() && nums[i] < track.getLast()) {
                continue;
            }
            used.add(nums[i]);
            track.add(nums[i]);
            backtack(nums,i+1);
            track.removeLast();
        }
    }
}

11. leetcode-51:N皇后

题目:n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

class Solution {
    //相当于两个集合组合在一起
    List<List<String>> res = new LinkedList<>();
    LinkedList<String> track = new LinkedList<>();
    public List<List<String>> solveNQueens(int n) {
        char[][] board = new char[n][n];
        for(int i=0; i<n; i++) {
            //填充每行
            Arrays.fill(board[i],'.');
        }
        backtrack(board,0);
        return res;
    }
    void backtrack(char[][] board, int row) {
        if(track.size() == board.length) {
            res.add(new LinkedList<>(track));
            return;
        }
        for(int i=0; i<board.length; i++) {
            //判断皇后是否相互攻击
            if(!isValid(board,row,i)) {
                continue;
            }
            board[row][i] = 'Q';
            track.add(new String(board[row]));
            backtrack(board,row+1);
            track.removeLast();
            board[row][i] = '.';
        }
    }
    boolean isValid(char[][] board, int row, int col) {
        int n = board.length;
        for(int i=0; i<n; i++) {
            if(board[i][col] == 'Q') return false;
        }
        for(int i=row-1,j=col-1; i>=0 && j>=0; i--,j--) {
            if(board[i][j] == 'Q') return false;
        }
        for(int i=row-1,j=col+1; i>=0 && j<n; i--,j++) {
            if(board[i][j] == 'Q') return false;
        }
        return true;
    }
}

12. leetcode-37:解数独

题目:编写一个程序,通过填充空格来解决数独问题。

class Solution {
    public void solveSudoku(char[][] board) {
        backtrack(board);
    }
    boolean backtrack(char[][] board) {
        for(int i=0; i<9; i++) { //遍历行
            for(int j=0; j<9; j++) { //遍历列
                if(board[i][j] != '.') {
                    continue;
                }
                for(char k='1'; k<='9'; k++) {
                    if(!isValid(board,i,j,k)) { //(i,j)位置放k是否合适
                        continue;
                    }
                    board[i][j] = k;
                    if(backtrack(board)) return true; //一个位置找到一个合适的数字立马返回
                    board[i][j] = '.';
                }
                return false; //9个数都试完了,没有true就返回false
            }
        }
        return true; //所有的都试完了,没有返回false,就返回true
    }
    boolean isValid(char[][] board, int row, int col, char n) {
        for(int i=0; i<9; i++) {
            if(board[row][i] == n) return false;
        }
        for(int i=0; i<9; i++) {
            if(board[i][col] == n) return false;
        }
        int rowStart = (row/3)*3;
        int colStart = (col/3)*3;
        for(int i=rowStart; i<rowStart+3; i++) {
            for(int j=colStart; j<colStart+3; j++) {
                if(board[i][j] == n) return false;
            }
        }
        return true;
    }
}

13. leetcode-22:括号生成

题目:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且有效的括号组合。

class Solution {
    List<String> res = new LinkedList<>();
    StringBuilder track = new StringBuilder();
    public List<String> generateParenthesis(int n) {
        backtrack(n,n);
        return res;
    }
    void backtrack(int left, int right) { //left为左括号剩余数量,right为右括号剩余数量,
        if(left < 0 ||right < 0) { 
            return;
        }
        if(left > right) { //[0,i]中左括号的数量肯定大于等于右括号
            return;
        }
        if(left == 0 && right ==0) {
            res.add(track.toString());
            return;
        }
        track.append('(');
        backtrack(left-1,right);
        track.deleteCharAt(track.length()-1);
        track.append(')');
        backtrack(left,right-1);
        track.deleteCharAt(track.length()-1);
    }
}

声明:转载自力扣网

posted @ 2022-04-21 21:42  盐小果  阅读(96)  评论(0编辑  收藏  举报