【LeetCode】491. 递增子序列

「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。

题目

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

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

示例 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]]
复制代码

提示

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

题解

思路

一个递增子序列 path 怎么产生?它的元素是从 nums 中一个个选的。 比如 [4,2,7,7],path 选第一个数,有 4 种选择:从 nums[0]nums[3]。 选了 nums[i] 之后,会在 nums[i+1] 到末尾的数中,再选下一个…… 以此类推,直到没得选为止。当 path 满足要求,就可以加入解集,这是递归回溯的思路。

定义递归函数:在从下标 start 到末尾的子数组中选合适的数推入 path。path 也作为参数。在递归过程中,不断选数字入 path。

递归函数中,通过 for 循环枚举出当前的所有选择——展开不同的递归分支

选择了一个数,推入 path,往下继续选择(继续递归)

当 start 指针到达边界,能选的都选了,没有数字可选了,结束递归

扫描二维码关注公众号,回复: 13686139 查看本文章

基于当前选择的递归,已经考察了所有可能,这时会回溯,path 要撤销最末尾的数字,切入别的分支

代码

const findSubsequences = (nums) => {
  const res = [];
  const len = nums.length;

  const dfs = (start, path) => {
    if (start == len) {    		// 递归的出口,指针已经越界
      if (path.length >= 2) {   // path长度大于等于2
        res.push(path.slice()); // 加入解集
        return;
      }
    }
    path.push(nums[start]);         // 进行选择
    for (let i = start + 1; i <= len; i++) { //枚举出选项,从start+1到len都可以选
      const prev = nums[start];     // 递归树中上一层的选择
      const cur = nums[i];          // 当前的选择
      if (i < len && cur == prev) { // i还没越界,且当前选择和上一层的选择相同
        dfs(i, path);               // 递归完当前选择,就break,避免i自增,导致i==len
        // 从而避免导致执行else if里的逻辑,导致start==len,导致来递归的出口,path推入res
        break;
      // i==len越界,让它落入递归,在递归的出口中return
      } else if (i == len || prev < cur) { 
        // 或prev < cur,满足条件,往下递归
        dfs(i, path);                      
      }
    }
    // 撤销选择
    path.pop();                     
  };
  for (let i = 0; i < len; i++) {
    dfs(i, []);
  }
  return res;
};
复制代码

结语

业精于勤,荒于嬉;行成于思,毁于随。

猜你喜欢

转载自juejin.im/post/7067395465762832391