回溯——491. 递增子序列

1 题目描述

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/increasing-subsequences

2 题目示例

示例 1:
输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

示例 2:
输入:nums = [4,4,3,2,1]
输出:[[4,4]]

3 题目提示

1 <= nums.length <= 15
-100 <= nums[i] <= 100

4 思路

可以用递归的方法实现二进制枚举,枚举出所有的子序列,然后判断是否合法。,我们可以得到这样的代码:

List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> temp = new ArrayList<Integer>();
public void dfs(int cur, int[] nums) {
    
    
    if (cur == nums.length) {
    
    
        // 判断是否合法,如果合法判断是否重复,将满足条件的加入答案
        if (isValid() && notVisited()) {
    
    
            ans.add(new ArrayList<Integer>(temp));
        }
        return;
    }
    // 如果选择当前元素
    temp.add(nums[cur]);
    dfs(cur + 1, nums);
    temp.remove(temp.size() - 1);
    // 如果不选择当前元素
    dfs(cur + 1, nums);
}

这是一个递归枚举子序列的通用模板,即用一个临时数组temp来保存当前选出的子序列,使用cur来表示当前位置的下标,在dfs(cur,nums)开始之前,[0, cur ―1]这个区间内的所有元素都已经被考虑过,而[cur, n]这个区间内的元素还未被考虑。在执行dfs(cur,nums)时,我们考虑cur这个位置选或者不选,如果选择当前元素,那么把当前元素加入到temp中,然后递归下一个位置,在递归结束后,应当把temp 的最后一个元素删除进行回溯;如果不选当前的元素,直接递归下一个位置。当然,如果我们简单地这样枚举,对于每一个子序列,我们还需要做一次O(n)的合法性检查和哈希判重复,在执行整个程序的过程中,我们还需要使用一个空间代价O(2")的哈希表来维护已经出现的子序列的哈希值。我们可以对选择和不选择做一些简单的限定,就可以让枚举出来的都是合法的并且不重复:

  • 使序列合法的办法非常简单,即给「选择」做一个限定条件,只有当前的元素大于等于上一个选择的元素的时候才能选择这个元素,这样枚举出来的所有元素都是合法的

  • 那如何保证没有重复呢?我们需要给「不选择」做一个限定条件,只有当当前的元素不等于上一个选择的元素的时候,才考虑不选择当前元素,直接递归后面的元素。因为如果有两个相同的元素,我们会考虑这样四种情况:

    1. 前者被选择,后者被选择
    2. 前者被选择,后者不被选择
    3. 前者不被选择,后者被选择
    4. 前者不被选择,后者不被选择

    其中第二种情况和第三种情况其实是等价的,我们这样限制之后,舍弃了第二种,保留了第三种,于是达到了去重的目的。

复杂度分析

  • 时间复杂度:o(2n*n)。仍然需要对子序列做二进制枚举,枚举出的序列虽然省去了判断合法性和哈希的过程,但是仍然需要O(n)的时间添加到答案中。
  • 空间复杂度:O(n)。这里临时数组的空间代价是O(n),递归使用的栈空间的空间代价也是O(n)。

5 我的答案

class Solution {
    
    
    List<Integer> temp = new ArrayList<Integer>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();

    public List<List<Integer>> findSubsequences(int[] nums) {
    
    
        dfs(0, Integer.MIN_VALUE, nums);
        return ans;
    }

    public void dfs(int cur, int last, int[] nums) {
    
    
        if (cur == nums.length) {
    
    
            if (temp.size() >= 2) {
    
    
                ans.add(new ArrayList<Integer>(temp));
            }
            return;
        }
        if (nums[cur] >= last) {
    
    
            temp.add(nums[cur]);
            dfs(cur + 1, nums[cur], nums);
            temp.remove(temp.size() - 1);
        }
        if (nums[cur] != last) {
    
    
            dfs(cur + 1, last, nums);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_44688973/article/details/125988321