Day17_最长递增子序列

每日一题

面试题:最长递增子序列(Longest Increasing Subsequence, LIS)
题目描述
给定一个整数数组 nums,找到其中最长严格递增子序列的长度(子序列不要求连续)。
示例:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,5,7,101],长度为 4。


答案与分析

1. 动态规划解法(时间复杂度 O(n²))
思路

  • 定义 dp[i] 表示以 nums[i] 结尾的最长递增子序列的长度。
  • 对每个 i,遍历 j0i-1,若 nums[i] > nums[j],则 dp[i] = max(dp[i], dp[j]+1)

代码实现

python

复制

def length_of_LIS(nums):
    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) if nums else 0

分析

  • 时间复杂度为 O(n²),空间复杂度 O(n)。
  • 优点:思路直观,易于理解。
  • 缺点:当 n 较大时(如 n=1e4),性能较差。

2. 贪心 + 二分查找优化(时间复杂度 O(n log n))
思路

  • 维护一个数组 tails,其中 tails[i] 表示长度为 i+1 的递增子序列的最小末尾值。
  • 遍历数组,对每个数进行二分查找,找到其在 tails 中的位置并更新或插入。

代码实现

python

复制

def length_of_LIS(nums):
    tails = []
    for num in nums:
        left, right = 0, len(tails)
        while left < right:
            mid = (left + right) // 2
            if tails[mid] < num:
                left = mid + 1
            else:
                right = mid
        if left == len(tails):
            tails.append(num)
        else:
            tails[left] = num
    return len(tails)

分析

  • 时间复杂度为 O(n log n),空间复杂度 O(n)。
  • 核心思想:通过维护 tails 数组,保证其严格递增。每次插入或替换时,使用二分查找优化效率。
  • 为什么能保证正确性?
    • 若当前数比 tails 所有元素大,说明可以形成更长的子序列。
    • 若当前数能替换 tails 中某个元素,说明可以优化后续子序列的构造(更小的末尾值更有利于后续扩展)。

面试考察点

  1. 动态规划基础:是否能设计状态转移方程。
  2. 算法优化能力:是否能想到贪心策略和二分查找的结合。
  3. 代码实现细节:二分查找的边界条件处理、数组更新逻辑。
  4. 复杂度分析:对时间/空间复杂度的理解和权衡能力。
posted @ 2025-03-19 00:01  Pikature  阅读(85)  评论(0)    收藏  举报