CSDN话题挑战赛第2期
参赛话题:算法题解
1.题目描述
给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
示例:
2.题目链接
来源:https://leetcode.cn/problems/increasing-subsequences
3.思路讲解
- 回溯法参数:startIndex —— 起始位置, list —— 存储路径的数组
- 结束条件:list 的长度等于大于 2,且满足递增,即可加入结果中
- 单层逻辑:判断当前层是否出现与当前节点重复的数字,未出现,加入当前节点
难点: 本题与组合问题非常像,都是很明显可以看出是使用回溯法的问题,但是本题我们不能再通过排序去重了,因为本题的结果是需要有顺序的,因此难点在于我们如何进行结果去重。
首先,我们来分析一下,产生重复答案的情况为:在同层节点(同一父节点的子节点)中,已经使用过的某一数字又出现了,以下图为例,当前层已经选择过 7 这个元素时,若是再选择 7,前面已经添加过一遍的 {4,7},又会再被添加一遍,因此对这种情况要进行剪枝。
去重具体操作方法:
通过 HashSet 记录当层已经使用过的节点,若判断已经 HashSet 中已经存在该元素,则剪枝
4.代码
class Solution {
//存放结果
List<List<Integer>> result = new ArrayList<>();
//暂存结果
List<Integer> path = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backTrack(nums, 0);
return result;
}
private void backTrack(int[] nums, int startIndex) {
if(path.size() > 1){
result.add(new ArrayList<>(path));//注意这⾥不要加return,因为要取树上的所有节点
}
HashSet<Integer> uset = new HashSet<>();//录本层元素是否重复使⽤,新的⼀层uset都会重新定义(清空)
for (int i = startIndex; i < nums.length; i++) {
//!path.isEmpty()&&nums[i]<path.get(path.size()-1)) : 保证子序列是递增的
//!uset.add(nums[i]) :保证在同一层不会重复使用相同数字
if ((!path.isEmpty()&&nums[i]<path.get(path.size()-1))||!uset.add(nums[i])) {
continue;
}
path.add(nums[i]);
backTrack(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
5.感想
只是一味的去被回溯法模板不是一劳永逸的,学会画回溯树,自己分析回溯法执行过程,剪枝过程,通过思考不断提升我们对题目的理解能力,一起加油吧,大家!