491.递归子序列
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
来源:LeetCode
网站:https://leetcode-cn.com/problems/increasing-subsequences/
版本一:递归枚举+Set去重
/**
* 算法一:递归枚举
* 思路:每一个位置的数,选择或不选择
*/
Set<List<Integer>> set = new HashSet<>();;
public List<List<Integer>> findSubsequences(int[] nums) {
if(nums == null || nums.length == 0) return new ArrayList<>();
dfs(nums,0,new ArrayList<>());//递归枚举
List<List<Integer>> ans = new ArrayList<>();//将set中的list取出来
for(List<Integer> ls : set){
ans.add(ls);
}
return ans;
}
private void dfs(int[] nums, int index, List<Integer> list){
if(index >= nums.length){
if (list.size() > 1)
set.add(new ArrayList(list));
return;
}
//选择当前数
if(list.size() == 0 || list.get(list.size()-1) <= nums[index]){//选择的第一个数 或者 cur >= pre
list.add(nums[index]);
dfs(nums,index+1,list);
list.remove(list.size()-1);
}
//不选择当前数
dfs(nums, index + 1, list);
}
算法一可以改进的地方:
1、list.get(list.size()-1) <= nums[index]
,所以每次都需要先判断list.size()
是否大于0
2、虽然实现了部分剪枝,但是仍然不能达到去重的效果,所以需要Set来筛选,效率不高。
版本二:递归枚举+剪枝
/**
* 算法二:递归枚举+剪枝
*/
List<List<Integer>> ans = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
public List<List<Integer>> findSubsequences2(int[] nums) {
if(nums == null || nums.length == 0) return new ArrayList<>();
dfs2(nums,0,Integer.MIN_VALUE);
return ans;
}
/**
* 1.前一个选择的数和当前数不同
* ① 如果当前数大于或等于前一个选择的数,选择当前数
* ② 不选择当前数
* 2.前一个选择的数和当前数相同
* ① 选择当前数
* ② 不选择当前数(可以省略,因为,前一个选择后一个不选择的结果与前一个不选择后一个选择的情况相同)
* 所以,这个问题中出现重复解的原因就是 2.②
*/
private void dfs2(int[] nums, int index,int lastVal){
if(index >= nums.length){
if (tmp.size() > 1)
ans.add(new ArrayList<>(tmp));
return;
}
//选择当前数
if (nums[index] >= lastVal){//剪枝,当前数需要比前一个选择的数大或等于
tmp.add(nums[index]);
dfs2(nums,index+1,nums[index]);
tmp.remove(tmp.size()-1);//回溯
}
//不选择当前数
if (lastVal != nums[index])//去重,如果前一个选择的数和当前的数相同,可以不用考虑这次不选择的情况
dfs2(nums,index+1,lastVal);
}
读者如果仍然无法理解
if (lastVal != nums[index])
,建议可以自己尝试着画一画递归树。