78. Subset && 90.Subset 2 (Array, DFS)

##### 78. Subsets 求不含重复数字的数组的子集

Given a set of distinct integers, nums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

Example:

Input: nums = [1,2,3]
Output:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

###### 方法一:DFS

使用深度遍历,首先我们有一个能返回所有子集的ArrayList res, 和一个临时变量ArrayList tmp, 当tmp满足一定条件的时候,往res里面添加结果,注意一开始添加的是空子集。深度遍历的重点是在回溯的时候要将最后新加入的节点删去。

引入一个int pos用来记录此子集的起点在哪,比如当pos = 1的时候就是从第二个元素往后循环添加元素(0 base),每次当此层用了第i个元素,那么下一层需要传入下一个元素的位置i+1 除此之外,当循环结束要返回上一层dfs的时候我们需要把这一层刚添加元素删去。

比如输入集合为[1,2,3]应当是这么运行,

[]

[1]

[1,2]

[1,2,3] //最底层子循环到头返回删去3,上一层的子循环也到头删去2

          //而此时,这一层循环刚到2,删去后还可以添加一个3

[1,3] //删除3,删除1

[2]

[2,3] //删除3,删除2

[3]
 

 public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        if(nums==null || nums.length==0)
            return res;
        List<Integer> temp = new ArrayList<Integer>();
        res.add(temp);//加入空子集
        Arrays.sort(nums);//先排序,可以保证升序输出,虽然本体没有要求,可以去掉这句,不过下一道题必须排序,因为要去除重复
        dfs(nums, 0, temp, res);
        return res;
    }
    
    private void dfs(int[] nums, int pos, List<Integer> temp, List<List<Integer>> res){
        for(int i=pos;i<nums.length;i++){
            temp.add(nums[i]);
            res.add(new ArrayList<Integer>(temp));//注意new一个新的list
            dfs(nums,i+1,temp,res);
            temp.remove(temp.size()-1);//回溯之前删节点
        }
    }

###### 方法二: 

在每一次的子集上添加新的元素,生成新的子集。

 public List<List<Integer>> subsets(int[] S) {
        List<List<Integer>> res = new ArrayList<>();
        res.add(new ArrayList<Integer>());//第一轮循环只有空集,所以只能添加1
        
        Arrays.sort(S);
        for(int i : S) {
            List<List<Integer>> tmp = new ArrayList<>();
            for(List<Integer> sub : res) {
                List<Integer> a = new ArrayList<>(sub);
                a.add(i);
                tmp.add(a);
            }
            res.addAll(tmp);
            System.out.println("tiis is i="+i+",tmp is"+tmp.toString());
        }
        return res;
    }

其效果是:

tiis is i=1,tmp is[[1]]

tiis is i=2,tmp is[[2], [1, 2]]

tiis is i=3,tmp is[[3], [1, 3], [2, 3], [1, 2, 3]] 

###### 方法三: 位运算

##### 90. Subset 2

Given a collection of integers that might contain duplicates, S, return all possible subsets.

Note: Elements in a subset must be in non-descending order. The solution set must not contain duplicate subsets.

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

分析:

这一题是上面Subset I的延伸,在这一题种,输入集合有重复的元素了,但是要求输出结果不能有重复的Set

例如,假设集合为[2,3,3],如果按照Subset I的程序不做改动,会出现什么情况呢

[]

[2]

[2,3]

[2,3,3]

[2,3] //把最后一个3删去,再把倒数第二个3删去,此时集合剩下[2],此层的循环还没完,还可以取最后一个,3,所以生成了重复的集合[2,3]

[3]

[3,3]

[3] //同理,把最后一个3删去,再把倒数第二个3删去,第一层循环还可以取最后一个数3,所以生成了重复的集合[3]

那么,我们需要做的是,在删去元素后,再取元素的时候,不要取和刚刚取过的元素相等的元素 即加上这么一条语句

while(i < num.length-1 && num[i] == num[i+1]) i++; 
 public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        if(nums==null || nums.length==0)
            return res;
        ArrayList<Integer> tmp = new ArrayList<>();
        Arrays.sort(nums);//注意要排序,这样才能避免重复
        res.add(tmp);
        dfs(nums, 0, tmp, res);
        return res;
    }
    private void dfs(int[] nums, int pos, List<Integer> tmp, List<List<Integer>> res){
        for(int i=pos;i<nums.length;i++){
            tmp.add(nums[i]);
            res.add(new ArrayList<>(tmp));
            dfs(nums, i+1,tmp,res);
            tmp.remove(tmp.size()-1);
            while(i<(nums.length-1) && nums[i]==nums[i+1]) i++;//和第一题的区别
        }
    }


refer:https://blog.csdn.net/u011095253/article/details/9158397 
 

猜你喜欢

转载自blog.csdn.net/shulixu/article/details/85886526
今日推荐