力扣:673. 最长递增子序列的个数

简介: 力扣:673. 最长递增子序列的个数

1. LIS


题源:最长递增子序列


最长递增子序列有两种做法:O(n2)的动态规划和O(nlogn)的贪心+二分。


1.1 动态规划


定义dp[i]为以第i个元素为结尾的最长上升子序列长度,其中num[i]必须被选择,那么状态转移方程为(dp初始化值为1):dp[i] = max(dp[j]) + 1; j < i, num[i] > num[j]


def findNumberOfLIS(nums):
    dp = [1] * len(nums)
    for i in range(1, len(nums)):
        for j in range(i):
            if nums[i] > nums[j]:
                dp[i] = max(dp[i], dp[j] + 1)
    return max(dp)
复制代码


复杂度分析:


  • 时间复杂度: O(n2)
  • 空间复杂度: O(n) dp[n]借助的空间


1.2 贪心 + 二分查找


我们要求得最长的上升子序列,那么我们应该保证序列上升的尽可能慢,因此每次添加到子序列后的数尽可能地小。 基于上面的想法,维护一个数组d[i],表示长度为i的最长上升子序列,且d[i]为满足长度为i的上升子序列的最小值,使用len记录数组d的元素个数,初始值d[0] = num[0]。


算法流程:


  • 遍历数组nums,当遍历到nums[i]时:
  • 若nums[i] > d[len],直接加入到数组末尾,len+1
  • 否则,在d数组中寻找第一个大于nums[i]的位置,将其插入


  • 以序列[0,2,5,3,7,101,18]
  • 第一步插入0:d = [0]
  • 第二步插入2:d = [0,2]
  • 第三步插入5:d = [0,2,5]
  • 第四步更新长度为3的最长子序列的末尾值:d = [0,2,3]
  • 第五步插入7:d = [0,2,3,7]
  • 第六步插入101:d = [0,2,3,7,101]
  • 第四步更新长度为5的最长子序列的末尾值:d = [0,2,3,7,18],得到最长递增子序列长度为5


def findNumberOfLIS(nums):
    d = []
    for n in nums:
        if not d or n > d[-1]:
            d.append(n)
        else:
            left, right = 0, len(d) - 1
            while left < right:
                mid = left + (right - left) // 2
                if d[mid] < n:
                    left = mid + 1
                else:
                    right = mid
            d[left] = n
    return len(d)
复制代码


复杂度分析:


  • 时间复杂度: O(nlogn) 使用了二分查找
  • 空间复杂度: O(n) d[n]借助的空间


2. 最长递增子序列的个数


2.1 动态规划


在LIS的基础上做进一步的求解:


定义dp[i]为以第i个元素为结尾的最长上升子序列长度,freq[i]为以第i个元素为结尾的最长上升子序列的个数。若nums的最长子序列长度为maxLen,那么答案为所有满足dp[i]=maxLen所对应的freq[i]之和。


dp[i] = max(dp[j]) + 1; j < i, num[i] > num[j]

freq[i]为所有满足dp[i]=dp[j] + 1的freq[j]之和


def findNumberOfLIS(nums):
    maxLen = 1
    freq = [1] * len(nums)
    dp = [1] * len(nums)
    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
                    freq[i] = freq[j]
                elif dp[j] + 1 == dp[i]:
                    freq[i] += freq[j]
        maxLen = max(maxLen, dp[i])
    sumFreq = 0
    for i in range(nums):
        if dp[i] == maxLen:
            sumFreq += freq[i]
    return sumFreq
复制代码


复杂度分析:


  • 时间复杂度: O(n2)
  • 空间复杂度: O(n) dp[n]借助的空间


相关文章
|
4月前
|
Python
【Leetcode刷题Python】376. 摆动序列
文章提供了解决LeetCode "摆动序列" 问题的Python实现代码,通过遍历整数数组并使用两个变量 down 和 up 来记录正差和负差摆动序列的长度,最终返回最长摆动子序列的长度。
41 0
|
4月前
|
Python
【Leetcode刷题Python】946. 验证栈序列
LeetCode题目“946. 验证栈序列”的Python解决方案,通过模拟栈的压入和弹出操作来验证给定的两个序列是否能通过合法的栈操作得到。
30 6
|
4月前
|
算法 Python
【Leetcode刷题Python】剑指 Offer 33. 二叉搜索树的后序遍历序列
本文提供了一种Python算法,用以判断给定整数数组是否为某二叉搜索树的后序遍历结果,通过识别根节点并递归验证左右子树的值是否满足二叉搜索树的性质。
23 3
|
4月前
|
Python
【Leetcode刷题Python】105. 从前序与中序遍历序列构造二叉树
LeetCode上105号问题"从前序与中序遍历序列构造二叉树"的Python实现,通过递归方法根据前序和中序遍历序列重建二叉树。
27 3
|
4月前
|
算法 Python
【Leetcode刷题Python】300. 最长递增子序列
LeetCode 300题 "最长递增子序列" 的两种Python解决方案:一种使用动态规划,另一种使用贪心算法结合二分查找。
39 1
|
4月前
|
算法 Java
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
43 0
|
4月前
|
Python
【Leetcode刷题Python】674. 最长连续递增序列
LeetCode 674题 "最长连续递增序列" 的Python解决方案,使用动态规划算法找出给定整数数组中最长连续递增子序列的长度。
95 0
|
6月前
|
存储 算法 数据可视化
哈希表法快速求解最长连续序列 | 力扣128题详细解析
哈希表法快速求解最长连续序列 | 力扣128题详细解析
|
6月前
|
Java
贪心 -力扣860.柠檬水找零力扣2208.将数组和减半的最少操作次数力扣179.最大数力扣376.摆动序列
贪心 -力扣860.柠檬水找零力扣2208.将数组和减半的最少操作次数力扣179.最大数力扣376.摆动序列
|
6月前
|
存储 算法
力扣经典150题第四十六题:最长连续序列
力扣经典150题第四十六题:最长连续序列
25 0