持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情
本文包含以下几题。
- 给定一个整数数组
nums,数组中元素互不相同,返回该数组的所有可能的组合。要求不能包含重复组合。 - 和上题类似,但数组可能包含重复元素,要求最终结果不能包含重复子集。
- 给定一个整数数组
nums,找出并返回数组中所有不同的递增子序列,子序列中至少两个元素。
解题思路
LeetCode 78 子集
本题是经典的回溯解决,和之前唯一的区别是之前都是寻找叶子节点,而本题注重寻找所有的二叉树结点,回溯的终止条件为当前起始索引等于数组的长度,在每次回溯之前都将结果加入最终集合即可,代码如下:
private List<List<Integer>> resSub = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtrace(nums, 0, new ArrayList<>());
return resSub;
}
public void backtrace(int[] nums, int cur, ArrayList<Integer> temp){
resSub.add(new ArrayList<>(temp));
if(cur==nums.length) return;
for(int i=cur;i<nums.length;i++){
temp.add(nums[i]);
backtrace(nums, i+1, temp);
temp.remove(temp.size()-1);
}
}
LeetCode 90 子集II
本题和上题几乎一致,但本题的数组中包含重复元素,如果按照上述思路求解则无法对子集去重,因此本题的关键在于去重,去重之前也做过,首先对数组进行排序,之后使用一个数组标记当前已使用的元素,之后通过i>0&&nums[i]==nums[i-1]&&!visited[i-1]即可判断。上面的去重方法使用了额外数组,实际上本题还可以使用此方式来去重:i>cur&&nums[i]==nums[i-1],这样保证了前后重复的元素在下一次回溯时会使用到。代码如下:
private List<List<Integer>> resSub2 = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
boolean[] visited = new boolean[nums.length];
backtrace2(nums, 0, new ArrayList<>(), visited);
return resSub2;
}
public void backtrace2(int[] nums, int cur, ArrayList<Integer> temp, boolean[] visited){
resSub2.add(new ArrayList<>(temp));
if(cur==nums.length) return;
for(int i=cur;i<nums.length;i++){
// 此处也可以不使用标记数组
// if(i>cur&&nums[i]==nums[i-1]) continue;
if(i>0&&nums[i]==nums[i-1]&&!visited[i-1]) continue;
temp.add(nums[i]);
visited[i] = true;
backtrace2(nums, i+1, temp, visited);
temp.remove(temp.size()-1);
visited[i] = false;
}
}
LeetCode 491 递增子序列
本题解题思路也是回溯,但数组中同样包含相同的元素,因此需要做去重,所需注意的是本题的数组并不是有序的,并且也不能将数组进行排序,因此前面的去重逻辑无法应用在本题。在回溯时,对于同一支二叉树的一层来说,如果本层包含相同元素,那在同一层必须做去重处理,否则必然导致重复,在提示中,题目显示数组的范围是-100-100,因此可以使用长度为201的数组来保存数组中元素的使用情况,因为是记录每层的使用情况,因此used数组不需要进行恢复,根据上述思路可得代码如下:
private List<List<Integer>> resFid = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtraceF(nums, 0, new ArrayList<>());
return resFid;
}
public void backtraceF(int[] nums, int cur, ArrayList<Integer> temp){
if(temp.size()>1){
resFid.add(new ArrayList<>(temp));
}
if(cur==nums.length) return;
int[] used = new int[201];
for(int i=cur;i<nums.length;i++){
if((temp.size()!=0&&temp.get(temp.size()-1)>nums[i])||used[nums[i]+100]==1) continue;
temp.add(nums[i]);
used[nums[i]+100] = 1;
backtraceF(nums, i+1, temp);
temp.remove(temp.size()-1);
}
}