原题链接: http://oj.leetcode.com/problems/subsets-ii/
比Subsets略微复杂一些的是这里的集合中可能出现反复元素,因此我们在求子集的时候要避免出现反复的子集。
在Subsets中我们每次加进一个元素就会把原来的子集都加上这个元素,然后再增加到结果集中。可是这样反复的元素就会产生反复的子集。为了避免这种反复,须要用个小技巧。
事实上比較简单,就是每当遇到反复元素的时候我们就仅仅把当前结果集的后半部分加上当前元素增加到结果集中,由于后半部分就是上一步中增加这个元素的全部子集,上一步这个元素已经增加过了。前半部分假设再加就会出现反复。所以算法上复杂度上没有提高,反而少了一些操作,就是遇到反复时少做一半。只是这里要对元素集合先排序。否则不好推断反复元素。相同的还是能够用递归和非递归来解,只是对于反复元素的处理是一样的。
递归的代码例如以下:
public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] num) { if(num == null) return null; Arrays.sort(num); ArrayList<Integer> lastSize = new ArrayList<Integer>(); lastSize.add(0); return helper(num, num.length-1, lastSize); } private ArrayList<ArrayList<Integer>> helper(int[] num, int index, ArrayList<Integer> lastSize) { if(index == -1) { ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>(); ArrayList<Integer> elem = new ArrayList<Integer>(); res.add(elem); return res; } ArrayList<ArrayList<Integer>> res = helper(num,index-1,lastSize); int size = res.size(); int start = 0; if(index>0 && num[index]==num[index-1]) start = lastSize.get(0); for(int i=start;i<size;i++) { ArrayList<Integer> elem = new ArrayList<Integer>(res.get(i)); elem.add(num[index]); res.add(elem); } lastSize.set(0,size); return res; }非递归的代码例如以下:
public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] num) { ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>(); res.add(new ArrayList<Integer>()); if(num==null || num.length==0) return res; Arrays.sort(num); int start = 0; for(int i=0;i<num.length;i++) { int size = res.size(); for(int j=start;j<size;j++) { ArrayList<Integer> newItem = new ArrayList<Integer>(res.get(j)); newItem.add(num[i]); res.add(newItem); } if(i<num.length-1 && num[i]==num[i+1]) { start = size; } else { start = 0; } } return res; }这样的NP问题的反复处理在LeetCode有一定出现频率,比方还有Permutations II也是这种,事实上本质就是当一个反复元素进来时忽略上一个元素已经有的结果,仅仅考虑由反复元素所产生的新结果。