一天一大 lee(递增子序列)难度:中等-Day20200825

20200825

题目:

给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是 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]。
  • 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。

抛砖引玉

抛砖引玉

暴力枚举

思路:
枚举数组的所有子集,在枚举过程中,新加入的元素大于等于最后一个元素

实现:

因为子集的元素在原数组中可以是不相邻的,则不能使用指针来划分子集

声明数组 temp 来作为中间子集,通过对数组元素的增加和删除来枚举原数组的子集:

  1. 枚举起点,起点之前元素都被枚举过(不代表都存放到中间数组中,及时递增的元素也枚举是也可以不同时存放)
  2. 从起点开始逐个判断其是否小于中间数组 temp 最后一个元素:
    • 大于等于放入 temp,那么存在两种情况:
      • 该元素可以放到的中间子集中参与后面集合的枚举(如果因为枚举元素的指针在递增,则递归改元素参与枚举后需要回溯中间数组来枚举当前起点下的后面的元素)
      • 该元素不参与后面集合的枚举,直接校验下一个元素
    • 小于不参与后面集合的枚举,直接校验下一个元素
    • 当还没从指定起点遍历到原数组末尾,中间数组就为空了,则重新推送起点元素
  3. 判断当前的中间数组是否满足要求:
    • 满足(长度大于 1,递增生产是已确定)则结果存放结果数组中
    • 因为原数组中存在重复元素则枚举是可能会出现相同子集被重复统计,则需要记录以及统计过的满足条件的子集

递归

  • 参数:枚举子集索引位:start,中间数组:temp
  • 终止条件:遇到枚举过的结果
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var findSubsequences = function (nums) {
  let _result = [],
    temp = [],
    len = nums.length,
    map = new Map()

  dfs(0, temp)

  function dfs(start, temp) {
    if (temp.length > 1) {
      let str = temp.toString()
      if (map.has(str)) return
      _result.push(str.split(','))
      map.set(str)
    }
    for (let i = start; i < len; i++) {
      let last = temp[temp.length - 1]
      if (temp.length == 0 || last <= nums[i]) {
        // 新元素参与后面子集枚举
        temp.push(nums[i])
        // 继续枚举子集,枚举子集索引位+1
        dfs(i + 1, temp)

        // 新元素不参与后面子集枚举
        temp.pop()
      }
    }
  }

  return _result
}

借助最后一个元素去重

  • 主要逻辑都是维护一个中间子集来存放递增数组

  • 上面在递归时记录了枚举到的索引,去重使用哈希记录结果

  • 之所以枚举子集是会出现相同的结果,是因为 nums 中存在重复元素,在枚举时不同位置的相同元素多次参与到子集中

  • 因为每次想中间子集中追加元素都是大于等于的,如果某个元素被放置到中间字中过,则记录其让其作为边界值

  • 枚举元素时(新元素:nums[index]、中间子集最后一个元素 last)如果:

    • nums[index] >= last,当前元素可以选择也可以不选择
    • 不选择的限制:
      • 如果 nums[index] == last,选择为:dfs(index + 1, nums[index], nums)
      • 如果 nums[index] == last,不选择为:dfs(index + 1, last, nums)
        可以发现选择和不选择逻辑重叠了,而且相等的情况已经统计了选择的情况,
        则需要限制不选择的条件为 nums[index] != last
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var findSubsequences = function (nums) {
  let _result = [],
    temp = []

  dfs(0, Number.MIN_VALUE, nums)

  function dfs(index, last, nums) {
    if (index === nums.length) {
      if (temp.length > 1) {
        _result.push(temp.toString().split(','))
      }
      return
    }

    // 满足递增当前元素添加到中间子集参与子集枚举
    if (nums[index] >= last) {
      temp.push(nums[index])
      dfs(index + 1, nums[index], nums)
      temp.pop()
    }
    // 不选择当前这个元素,子集继续向后枚举
    if (nums[index] != last) {
      dfs(index + 1, last, nums)
    }
  }

  return _result
}

博客: 小书童博客

每天的每日一题,写的题解会同步更新到公众号一天一大 lee 栏目
欢迎关注留言

公号: 坑人的小书童

坑人的小书童

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值