LeetCode78-子集
1.题意
给定一个无重复元素
的数组,求其所有的子集
。
2.分析
- 注意关键词
所有的,全部的
- 一道非常经典的
回溯题目(深度搜索)
- 一个简单的例子分析递归树
- 按照如图的递归树,进行深度搜索即可
- 一个关键的问题,
如何避免重复?
{1,2}和{2,1}首先对数组排序
其次利用一个索引标记位,如果访问了1,那么后面只能从2开始访问,也就是访问过的元素不再访问
3.代码
public static List<List<Integer>> subsets(int[] nums) {
// write your code here
Arrays.sort(nums);
int startIndex = 0;
List<Integer> subset = new ArrayList<>();
List<List<Integer>> results = new ArrayList<>();
subsetHelper(nums, startIndex, subset, results);
return results;
}
public static void subsetHelper(int[] nums,
int startIndex,
List<Integer> subset,
List<List<Integer>> results){
//添加当前的subsets到结果集 注意深拷贝
results.add(new ArrayList<>(subset));
//从当前数组的位置 往后遍历
for (int i = startIndex; i < nums.length; i++){
subset.add(nums[i]);
subsetHelper(nums, i+1, subset, results);
// 移除当前subset的最后一个元素
subset.remove(subset.size() - 1);
}
}
LeetCode90-子集2
1.题意
给定数组,含重复元素
,求其所有的子集
。
2.分析
- 主题思路肯定和上题一样
- 关键的问题在于如何去重
- 例如对于: 1 , 2 ′ , 2 ′ ′ , 2 ′ ′ ′ {1,2',2'',2'''} 1,2′,2′′,2′′′
- 按照顺序来添加到subsets,也就是说不同的2只能按位置添加到子集中,如果中间有越位,则会产生相同的集合
- { 1 , 2 ′ } 和 { 1 , 2 ′ ′ } \{1,2'\}和 \{1,2''\} {1,2′}和{1,2′′} 就是重复元素
- { 1 , 2 ′ , 2 ′ ′ } 和 { 1 , 2 ′ , 2 ′ ′ ′ } \{1,2',2''\}和\{1,2',2'''\} {1,2′,2′′}和{1,2′,2′′′}也是重复元素
- 也就是说添加重复元素的时候,只能从前往后逐次添加不能跳跃,遇到跳跃的情况要
在递归的过程中处理掉
3.代码
public static List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
// write your code here
List<List<Integer>> results = new ArrayList<>();
if (nums == null || nums.length == 0){
return results;
}
int startIndex = 0;
List<Integer> subset = new ArrayList<>();
subsetsHelper(nums, startIndex, subset, results);
return results;
}
private static void subsetsHelper(int[] nums,
int startIndex,
List<Integer> subset,
List<List<Integer>> results){
results.add(new ArrayList<>(subset));
for (int i=startIndex; i<nums.length; i++){
// 当判断出当前的 i 与其左边的元素相同
// 且nums[i-1] 未被放入 subsets中 则会产生重复 继续下一次循环
// 如果 nums[i-1] 被放入了subsets中
// 则 subsetsHelper(nums, i+1, subset, results); 中下一次的 startIndex = i
// 因为 nums[i-1]所以有可能造成访问数组越界 所以i != 0
if (i != 0 && nums[i] == nums[i-1] && i != startIndex){
continue;
}
subset.add(nums[i]);
subsetsHelper(nums, i+1, subset, results);
subset.remove(subset.size() - 1);
}
}
小结
-
遇到
全部,所有
等关键词,一般考虑深度搜索,递归,回溯等方法
-
递归的过程中,去除重复元素或者题目指定的特定元素是常见的情况
不能先把所有的情况都求出来后,再去除
,这样会导致时间很长要在递归的过程中进行剔除,也叫做剪枝的过程
-
回溯的实现,其实就是三行代码:
subset.add(nums[i]); subsetsHelper(nums, i+1, subset, results); subset.remove(subset.size() - 1);