Leetcode Backtracking(回溯算法) 刷题笔记

760 阅读5分钟

78. Subsets

该题给我们一个array,要求我们找到所有不重复的子集

输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

这里我们运用backtracking算法,先sort array在此题中其实并无意义,建立一个backtrack方法,在方法中,先建立一个新的ArrayList,然后用for loop添加新的元素,用递归向更深处搜索,搜索完后删除新添加的元素

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

90. Subsets II

该题与78几乎一样,但是涉及到了重复问题,这里我们先将array sort,然后比较当前位与前一位,如果相同则跳过当前位。

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(nums);
        backtrack(list, new ArrayList<>(), nums, 0);
        return list;
    }
    private void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] nums, int start){
        list.add(new ArrayList<>(tempList));
        for(int i = start; i < nums.length; i++){
            if(i > start && nums[i] == nums[i-1]) continue;
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, i + 1);
            tempList.remove(tempList.size()-1);
        }
    }
}

77. Combinations

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

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

仍然采用backracking,写一个backtrack helper function,然后检测是否size是k,如果是k则把该tempList加入result list

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> list = new ArrayList<>();
        if(n == 0 || k == 0) return list;
        backtrack(list, new ArrayList<>(), 0, n, k);
        return list;
    }
    private void backtrack(List<List<Integer>> list, List<Integer> tempList, int start, int n, int k){
        if(tempList.size() == k) list.add(new ArrayList<>(tempList));
        for(int i=start; i<n; i++){
            tempList.add(i+1);
            backtrack(list, tempList, i+1, n, k);
            tempList.remove(tempList.size()-1);
        }
    }
}

39. Combinations Sum

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

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

该题与之前的题目思路完全相同,只不过判定条件为target当前与0的关系,如果等于0则直接添加,小于0则剪枝,大于0则继续向下探索,难点在于,递归时的i为0,因为每个数字可以取无限次

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> list = new ArrayList<>();
        backtrack(list, new ArrayList<>(), candidates, target, 0);
        return list;
    }
    private void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] candidates, int target, int start){
        if(target < 0) return;
        else if(target == 0) list.add(new ArrayList<>(tempList));
        else{
            for(int i=start; i<candidates.length; i++){
                tempList.add(candidates[i]);
                backtrack(list, tempList,candidates, target-candidates[i], i);
                tempList.remove(tempList.size()-1);
            }
        }
    }
}

40. Combinations Sum II

思路完全相同,先sort然后判定当前的数字与前一个数字是否相等,如果相等则跳过

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> list = new ArrayList<>();
        Arrays.sort(candidates);
        backtrack(list, new ArrayList<>(), candidates, target, 0);
        return list;
    }
    private void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] candidates, int target, int start){
        if(target < 0) return;
        else if(target == 0) list.add(new ArrayList<>(tempList));
        else{
            for(int i=start; i<candidates.length; i++){
                if(i > start && candidates[i] == candidates[i-1]) continue;
                tempList.add(candidates[i]);
                backtrack(list, tempList, candidates, target-candidates[i], i+1);
                tempList.remove(tempList.size()-1);
            }
        }
        
    }
}

216. Combinations Sum III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字

77 题和 40 题的结合题,需要统计还剩余的n,而且要判定当前的tempList的size是否为k,可以直接用一个1-9的array也可以直接用进for loop

class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>> list = new ArrayList<>();
        if(n == 0 || k == 0) return list;
        backtrack(list, new ArrayList<>(), n, k, 0);
        return list;
    }
    private void backtrack(List<List<Integer>> list, List<Integer> tempList, int n, int k, int start){
        if(n == 0){
            if(tempList.size() == k) list.add(new ArrayList<>(tempList));
            else return;
        }
        else if(n < 0) return;
        else{
            for(int i = start; i < 9; i++){
                tempList.add(i+1);
                backtrack(list, tempList, n-i-1, k, i+1);
                tempList.remove(tempList.size()-1);
            }
        }
    }
}

377. Combinations Sum IV

这道题可以用backtracking的做法,但是会超时,该用dp解法

dp[i] = 每一个能到dp[i]的step的数量总和

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target+1];
        dp[0] = 1;
        for(int i = 0; i < dp.length; i++){
            for(int num:nums){
                dp[i] += dp[i-num];
            }
        }
        return dp[target];
    }
}

46. Permutations

给定一个不重复的数组,返回所有的排列组合

输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

Main function 与之前所有题目相同,判定条件为是否长度为nums.length,为true则获取了全部元素,在for loop中要判定当前的tempList里是否已经包含了当前元素,如果包含则跳过(剪枝)

class Solution {
    public List<List<Integer>> permutations(int[] nums) {
        List<List<Integer>> list = new ArrayList<>();
        backtrack(list, new ArrayList<>(), nums);
        return list;
    }
    private void backtrack(List<List<Integer>> list, List<Integer> tempList, int[] nums){
        if(tempList.size() == nums.length) list.add(new ArrayList<>(tempList ));
        for(int i = 0; i < nums.length; i++){
            if(tempList.contains(nums[i])) continue;
            tempList.add(nums[i]);
            backtrack(list, tempList, nums);
            tempList.remove(tempList.size()-1);
        }
    }
}