首页 > 其他分享 >回溯-子集

回溯-子集

时间:2022-11-01 19:45:20浏览次数:37  
标签:cur nums int 元素 子集 回溯 path

组合是无序的,满足方案要求即可,对应不同的题意,有时候元素可以重复,有时候不能重复。

排列是有序的,同一批元素可以有多种排列。

子集跟上面两种又不同,首先空集是子集,一个元素也是子集,数组本身也是子集的子集。

所以子集的求解更复杂。

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

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
我们要解决以下问题
1.对应子集,怎样是可行解?
答案是对应数组中的元素,有或者没有,都是一个可行解
2.什么情况下递归结束?
因为不确定子集中的元素,所以我们需要自己定义一个指针cur,每进入一次新的递归,cur向右移动一位,当cur到达数组末尾的时候就结束递归。
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {

        //定义一个指针cur,从前往后移动,cur==length,递归结束
        dfs(nums, 0);
        return res;

    }

    private void dfs(int[] nums, int cur) {
         res.add(new ArrayList<>(path));
        //递归到底
        if (cur == nums.length) {
            return;
        }
        //取当前位置的元素
        for(int i=cur;i<nums.length;i++){
            path.add(nums[i]);
            dfs(nums, i + 1);

        //不取当前位置的元素
            path.remove(path.size() - 1);
        }
        

    }
}

案例2:

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

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

本题目是案例1的进阶版,难点在于数组中有重复元素,子集中不能有重复子集。

这是一个典型的回溯算法,非常经典,因为它基本上用到了回溯需要的所有手段

一、回溯的跳出
大家知道,回溯就是递归版的穷举,这个不难,关键点在于递归何时退出?
我们知道这个题目是要所有子集,对于结果并没有元素的长度限制。
因此我们只能自己设置一个指针,每得到一个元素,指针右移一位,当指针指到最后一个元素时,本轮递归就到底了!

二、子集肯定是不能重复的
因为是子集,所以一个元素不能使用两次,不重复使用同一个元素,要求我们定义一个used数组,上层递归中使用过的元素,下层的递归不允许再用!

三、不能有重复的子集
前面两步估计大部分人都能搞定,难在第三点,数组中有重复元素,如果仅仅简单的穷举,生成的子集一定会重复。
但题目要求不能有重复子集。这个就要求我们在合适的地方进行剪枝,关键是在哪里减呢?
如果有一个元素跟它前面的元素相等,比如[1,2(1),2(2)],
当遍历到2(2)时,发现前面的2(1)还没用过,那么这个地方就该剪掉!
原因是什么?
本轮遍历的后面的2(2)了,前面的2(1)还没有用过,说明在上一轮循环中已经出现过一次[1,2(1)]了
所以这里如果不剪枝,肯定会跟前面的重复。

class Solution {
   List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        List<Integer> path = new ArrayList<>();
        int n = nums.length;
        boolean[] used = new boolean[n];
        dfs(nums, path, used, 0);
        return res;
    }

    private void dfs(int[] nums, List<Integer> path, boolean[] used, int cur) {
        res.add(new ArrayList<>(path));
        if (cur == nums.length) {
            return;
        }
        for (int i = cur; i < nums.length; i++) {
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
                continue;
            }
            if (!used[i]) {
                path.add(nums[i]);
                used[i] = true;
                dfs(nums, path, used, i + 1);
                path.remove(path.size() - 1);
                used[i] = false;
            }
        }

    }
}

 

标签:cur,nums,int,元素,子集,回溯,path
From: https://www.cnblogs.com/wangbin2188/p/16848903.html

相关文章

  • 回溯-排列
    跟组合类似,排列也是穷举所有可行解,区别在于排列是有序的,同一个组合可以有多种排列。比如对组合来说[1,2,3]和[3,2,1]是同一个,但对于排列而言,就是两个。案例1:给定一个不含......
  • 回溯-组合
    组合问题也是需要进行穷举的,使用回溯算法正合适。案例1:给你一个无重复元素的整数数组 candidates和一个目标整数 target ,找出 candidates 中可以使数字和为目标......
  • 回溯-单词搜索
    在二维数组进行单词搜索也是经典的需要采用回溯算法的问题。案例1:给定一个 mxn二维字符网格 board和一个字符串单词 word。如果 word存在于网格中,返回true;否......
  • 回溯-N皇后
    回溯算法其实就是暴力穷举算法,只不过暴力穷举采用了先拆解子问题,然后将子问题使用递归的方式进行求解,并且在不满足条件的情况下,有向上回溯的过程。许多复杂的,规模较大的问......
  • LeetCode 236. 二叉树的最近公共祖先 - 回溯的理解
    题目https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/思路自己做只摸到一些门道,还是靠随想录...代码:deflowestCommonAncestor(self,root:'......
  • 1994. 好子集的数目
    题目描述给一个整数的数组nums,数组中的元素不超过30,数组长度不超过\(10^5\)给出了好子集的定义(数组中所有元素成绩可以拆分为互不相同质数的积)对好子集个数的要求是不同......
  • leetcode(力扣) 78. 子集(回溯 & 巧妙解法)
    文章目录​​题目描述​​​​法一(巧妙暴力解)​​​​思路分析​​​​完整代码​​​​法二(回溯):​​​​思路分析​​​​完整代码​​题目描述给你一个整数数组nums......
  • leetcode(力扣) 491. 递增子序列(回溯 & 去重思路)
    文章目录​​题目描述​​​​思路分析​​​​完整代码​​题目描述给你一个整数数组nums,找出并返回所有该数组中不同的递增子序列,递增子序列中至少有两个元素。你可以......
  • Leetcode 90. 子集 II
    给你一个整数数组nums,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。返回的解集中,子集可以按任意顺序排列。示例1:输入:nums=[1......
  • 代码随想录day28 | 93. 复原 IP 地址 78.子集 90. 子集 II
    93.复原IP地址题目|文章思路1.先判断每个部分是不是符合要求2.如果符合要求则加入path,递归下一层3.当遍历到字符串末尾,如果有四层,则加入结果,否则直接返回。实现......