递增子序列的相关题目总结

目录

1. 最长连续递增序列

2. 最长上升子序列

3、最长递增子序列的个数

4、递增子序列

5、最长连续序列


这里总结一些递增子序列的相关题目,有的题目中会有最优解,但是这里主要关注的是动态规划的做法,所以只写出动态规划或者通用的一些方法。

介绍这些题目之前,先分清一下什么是子序列,什么是子串

子序列:子序列是不连续的,如序列 [4, 6, 5] 是 [1, 2, 4, 3, 7, 6, 5] 的一个子序列

子串:子串是连续的

1. 最长连续递增序列

输入: [1,3,5,4,7]
输出: 3
解释: 最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为5和7在原数组里被4隔开。 

这道题说的是递增序列,但是要求是连续的,所以其实是求递增子串

class Solution:
    def findLengthOfLCIS(self, nums: List[int]) -> int:
        if not nums:
            return 0
        if len(nums) == 1:
            return 1
        dp = [1]*len(nums) #代表连续递增序列的长度
        for i in range(1,len(nums)):
            if nums[i]>nums[i-1]: #如果后一个元素大于前一个元素,那长度+1
                dp[i] = dp[i-1]+1
        return max(dp) # 找出最大的那个数就是长度,不能是dp[-1],dp[-1]代表的是以最后一个元素结尾是最长连续递增序列,但是最后一个并不一定是最大的数。

2. 最长上升子序列

使用动态规划来做,定义dp数组表示子序列的长度,dp[i] 表示在第i 个元素之前的任意一个元素为起点,到第i个元素结尾的上升子序列的长度,初始化都为1,表示每个元素都至少可以单独成为子序列,此时长度都为 1。

遍历到每个元素 nums[i] 后,再从头遍历一下[0-i] 里的这些元素nums[j],如果nums[i]>nums[j],那nums[i]这个元素就可以接到nums[j] 这个元素的后面,即 dp[i] = dp[j] + 1 

所以递归方程就是  dp[i] = max(dp[i],dp[j]+1) 0\leq j<i,最后依旧是找max(dp),而不是dp[-1]

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums:
            return 0
        dp=[1]*len(nums)
        for i in range(len(nums)):
            for j in range(i):
                if nums[i]>nums[j]:
                    dp[i] = max(dp[i],dp[j]+1)
        return max(dp)

这道题的内层循环如果每次都从0开始遍历,其实有很多重复的遍历,所以内层循环可以使用二分法来查找,找到第一个比num[i]大的数,然后替换成num[i],具体的不多说,代码如下:

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        res = []
        for num in nums:
            if not res or num>res[-1]:
                res.append(num)
            else:
                l=0
                r=len(res)-1
                while l<r:
                    mid = (l+r)//2
                    if res[mid]<num:
                        l = mid+1
                    else:
                        r = mid
                res[l]=num
        return len(res)

3、最长递增子序列的个数

这道题和上面那道题很像,只不过多了一个统计最长递增子序列的个数

class Solution:
    def findNumberOfLIS(self, nums: List[int]) -> int:
        if not nums:
            return 0
        dp = [1]*len(nums)
        count = [1]*len(nums)
        temp = 1
        res = 0
        for i in range(1,len(nums)):
            for j in range(i):
                if nums[i]>nums[j]:
                    if dp[j]+1>dp[i]:
                        dp[i] = dp[j]+1
                        count[i] = count[j]
                    elif dp[i] == dp[j]+1:
                        count[i]+=count[j]
            # temp = max(temp,dp[i])
        temp = max(dp)
        for i in range(len(nums)):
            if dp[i] == temp:
                res += count[i]
        return res

当 dp[j]+1 > dp [i] 时,说明第一次找到了新的长度,为dp[j]+1,这个时候count[i] = count[j],表明以 nums[i] 结尾的最长递增子序列的组合方式就等于 nums[j] 目前的组合方式

当 dp[j]+1 = dp [i] 时,说明这个长度的递增序列已找到过一次了,则 count[i]+=count[j],现有的组合方式个数加上 count [j] 的组合方式,即为总的组合方式个数

最后一次遍历是找到最长的那个长度,它所对应的count就是结果

这道题还可以用树状数组,目前能力有限,还不会树状数组,跳过

4、递增子序列

这道题不再是使用动态规划,而是深度优先搜索,类似于回溯,如果使用一般的回溯解法,耗时过慢,代码如下

class Solution:
    def __init__(self):
        self.res = []
    def findSubsequences(self, nums: List[int]) -> List[List[int]]:
        
        def dfs(temp,start,nums):
            if len(temp)>=2 and temp not in self.res:
                self.res.append(temp[:])
            for i in range(start,len(nums)):
                if not temp or nums[i]>=temp[-1]:
                    temp.append(nums[i])
                    dfs(temp,i+1,nums)
                    temp.pop()
        temp = []
        dfs(temp,0,nums)
        return self.res

可以看到耗时六千多毫秒,使用集合来进行剪枝依旧耗时过长,可以使用字典来剪枝。

class Solution:
    def findSubsequences(self, nums: List[int]) -> List[List[int]]:
        res = []
        def dfs(start, tmp):
            dic = {}
            if len(tmp) > 1:
                res.append(tmp)
            for i in range(start, len(nums)):
                if dic.get(nums[i], 0):
                    continue

                if len(tmp) == 0 or nums[i] >= tmp[-1]:
                    dic[nums[i]] = 1
                    dfs(i + 1, tmp + [nums[i]])


        dfs(0, [])
        return res

耗时一下子就降下来了。

5、最长连续序列

先空着,学习了并查集再来完善

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值