问题

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是 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]这个数组来举例,抽象为树形结构如图:
leetcode-491:递增子序列 - 图1

回溯三部曲

  • 递归函数参数

    • 本题求子序列,很明显一个元素不能重复使用,所以需要startIndex,调整下一层递归的起始位置
      1. List<List<Integer>> result = new ArrayList<>();
      2. List<Integer> path = new ArrayList<>();
      3. public void backtracking(int[] nums, int startIndex)
  • 终止条件

    • 本题其实类似求子集问题,也是要遍历树形结构找每一个节点,可以不加终止条件,startIndex每次都会加1,并不会无限递归
    • 但本题收集结果有所不同,题目要求递增子序列大小至少为2,所以代码如下:
      1. if (path.size() > 1) {
      2. result.add(new ArrayList<>(path));
      3. // 注意这里不要加return,因为要取树上的所有节点
      4. }
  • 单层搜索逻辑

leetcode-491:递增子序列 - 图2
在图中可以看出,同层上使用过的元素就不能在使用了,注意这里和子集Ⅱ中去重的区别

本题只要同层重复使用元素,递增子序列就会重复,而子集Ⅱ中是排序之后看相邻元素是否重复使用

还有一种情况就是如果选取的元素小于子序列最后一个元素,那么就不能是递增的,所以也要pass掉

  1. //!path.isEmpty()&&nums[i]<path.get(path.size()-1)) : 保证子序列是递增的
  2. // uset.contains(nums[i]) :保证在同一层不会重复使用相同数字
  3. if ((!path.empty() && nums[i] < path.get(path.size() - 1)) || uset.contains(nums[i])) {
  4. continue;
  5. }
  1. //记录本层元素是否重复使⽤,新的⼀层uset都会重新定义
  2. HashSet<Integer> uset = new HashSet<>();
  3. for (int i = startIndex; i < nums.length; i++) {
  4. if ((!path.isEmpty() && nums[i] < path.get(path.size()-1)) || uset.contains(nums[i])){
  5. continue;
  6. }
  7. uset.add(nums[i]);
  8. path.add(nums[i]);
  9. backTrack(nums, i + 1);
  10. path.remove(path.size() - 1);
  11. }
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;
    }
}