力扣78题、77题、90题、491题(回溯算法、子集组合)

78、子集

基本思想:

回溯算法

具体实现:

这道题没有if判断递归是否结束

因为这个是每递归一下就加入到结果数组中,

代码: 

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> subsets(int[] nums) {
        if (nums.length == 0){
            result.add(new ArrayList<>());
            return result;
        }
        subsetsHelper(nums, 0);
        return result;
    }
    private void subsetsHelper(int[] nums, int startIndex){
        result.add(new ArrayList<>(path));
        for (int i = startIndex; i < nums.length; i++){
            path.add(nums[i]);
            subsetsHelper(nums,i+1);
            path.removeLast();
        }
    }
}

 

77、组合

基本思想:

回溯算法

具体实现:

1、组合是不能有重复的

递归函数的参数每次就有起始数,

for循环从start开始递增

上述两个操作保证了不会有重复

2、递归结束条件是到达叶子节点

 

 

代码:

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

 

剪枝:

 

 具体实现:

k-len(track)是track中还差的元素个数

n-(k-len(track))+1是要达到组合中元素为k,至多开始的位置

比如:n=4,k=3 ,track里面啥也没    4-3+1=2

          意思是最多从2开始,组合可以是2,3,4

          再循环到3不可能满足组合个数

python写到代码里n-(k-len(track))+2 因为range的第二个参数代表第一个不取的数

代码:

class Solution {
    List<List<Integer>> result  = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combine(int n, int k) {
        combineHelper(n, k, 1);
        return result;
    }
    private void combineHelper(int n, int k, int startIndex){
        if (path.size() == k){
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i <= n - (k - path.size()) + 1; i++){
            path.add(i);
            combineHelper(n, k, i + 1);
            path.removeLast();
        }
    }
}

 

 90、子集II

基本思想:

回溯算法

与78题的区别是,子集需要去重

具体实现:

 

 

代码:

class Solution {
    List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
    LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果   
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        if (nums.length == 0){
            result.add(path);
            return result;
        }
        Arrays.sort(nums);//去重需要排序
        subsetsWithDupHelper(nums,0);
        return result;
    }
    private void subsetsWithDupHelper(int[] nums, int startIndex){
        result.add(new ArrayList<>(path));
        if (startIndex >= nums.length){
            return;
        }
        for (int i = startIndex; i < nums.length; i++){
            if (i > startIndex && nums[i] == nums[i - 1]){
                //我们要对同一树层使用过的元素进行跳过
                continue;
            }
            path.add(nums[i]);
            subsetsWithDupHelper(nums, i + 1);
            path.removeLast();
        }
    }
}

 

 

 

 

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> track = new LinkedList<>();
    boolean[] used;
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        used = new boolean[nums.length];
        traversal(nums, 0);
        return result;
    }
    public void traversal(int[] nums, int start) {
        result.add(new ArrayList<>(track));
        for (int i = start; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue;
            //前面有相同的元素并且没有被使用过,那就是同一树层碰到相同元素了
            track.add(nums[i]);
            used[i] = true;
            traversal(nums, i + 1);
            track.removeLast();
            used[i] = false;
        }
    }
}

 

 491、递增子序列

基本思想:

回溯算法

与90题不同,90题需要排序,这道题不需要

90题判断是否重复是用nums[i] == nums[i-1],这道题不行

具体实现:

 

 

代码:

 

class Solution {
    private List<Integer> path = new ArrayList<>();
    private List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> findSubsequences(int[] nums) {
        backtracking(nums,0);
        return res;
    }

    private void backtracking (int[] nums, int start) {
        if (path.size() > 1) {
            res.add(new ArrayList<>(path));//注意这里不要加return,要取树上的节点
        }
    //记录[-100,100]这些数字出现的个数
    //[-100,100]对应索引[0,201]
        int[] used = new int[201];
        for (int i = start; i < nums.length; i++) {
            if (used[nums[i] + 100] == 1) continue;
            if (!path.isEmpty() && nums[i] < path.get(path.size() - 1)) continue;
            used[nums[i] + 100] = 1;//记录这个元素在本层用过了,本层后面不能再用了
            path.add(nums[i]);
            backtracking(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}

 

 

posted @ 2021-03-31 16:27  最近饭吃的很多  阅读(115)  评论(0编辑  收藏  举报