问题
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是 2
示例:
输入:[4, 6, 7, 7]
输出:[[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
说明:
- 给定数组的长度不会超过15
- 数组中的整数范围是 [-100,100]
- 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况
思路
这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列
这又是子集,又是去重,类似于子集Ⅱ,在这道题中,中我们是通过排序,再加一个标记数组来达到去重的目的
而本题求自增子序列,是不能对原数组进行排序的,排完序的数组都是自增子序列了,所以不能使用之前的去重逻辑
为了有鲜明的对比,我用[4, 7, 6, 7]
这个数组来举例,抽象为树形结构如图:
回溯三部曲
递归函数参数
- 本题求子序列,很明显一个元素不能重复使用,所以需要
startIndex
,调整下一层递归的起始位置List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public void backtracking(int[] nums, int startIndex)
- 本题求子序列,很明显一个元素不能重复使用,所以需要
终止条件
- 本题其实类似求子集问题,也是要遍历树形结构找每一个节点,可以不加终止条件,
startIndex
每次都会加1,并不会无限递归 - 但本题收集结果有所不同,题目要求递增子序列大小至少为
2
,所以代码如下:if (path.size() > 1) {
result.add(new ArrayList<>(path));
// 注意这里不要加return,因为要取树上的所有节点
}
- 本题其实类似求子集问题,也是要遍历树形结构找每一个节点,可以不加终止条件,
单层搜索逻辑
在图中可以看出,同层上使用过的元素就不能在使用了,注意这里和子集Ⅱ中去重的区别
本题只要同层重复使用元素,递增子序列就会重复,而子集Ⅱ中是排序之后看相邻元素是否重复使用
还有一种情况就是如果选取的元素小于子序列最后一个元素,那么就不能是递增的,所以也要pass掉
//!path.isEmpty()&&nums[i]<path.get(path.size()-1)) : 保证子序列是递增的
// uset.contains(nums[i]) :保证在同一层不会重复使用相同数字
if ((!path.empty() && nums[i] < path.get(path.size() - 1)) || uset.contains(nums[i])) {
continue;
}
//记录本层元素是否重复使⽤,新的⼀层uset都会重新定义
HashSet<Integer> uset = new HashSet<>();
for (int i = startIndex; i < nums.length; i++) {
if ((!path.isEmpty() && nums[i] < path.get(path.size()-1)) || uset.contains(nums[i])){
continue;
}
uset.add(nums[i]);
path.add(nums[i]);
backTrack(nums, i + 1);
path.remove(path.size() - 1);
}
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public void backtracking(int[] nums, int startIndex) {
if (path.size() > 1) {
result.add(new ArrayList<>(path));
// 注意这里不要加return,要取树上的节点
}
HashSet<Integer> uset = new HashSet<>(); // 使用set对本层元素进行去重
for (int i = startIndex; i < nums.length; i++) {
if ((!path.isEmpty() && nums[i] < path.get(path.size() - 1))
|| uset.contains(nums[i])) {
continue;
}
uset.add(nums[i]);
path.add(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了
backtracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums, 0);
return result;
}
}