返回

一文读懂动态规划中的“最长上升子序列”及其时间复杂度优化

后端

动态规划在计算机科学中是一种重要的算法范式,它能有效解决一系列优化问题。其中,“最长上升子序列”问题是动态规划的经典应用之一。本文将深入剖析该问题,并探索如何对其时间复杂度进行优化。

问题

最长上升子序列问题给定一个正整数序列,目标是找到其中长度最长的单调递增子序列。例如,对于序列 [3, 4, -1, 5, 8, 3, 6, 2, 7],最长上升子序列为 [3, 4, 5, 8, 6, 7]。

动态规划法

动态规划法解决最长上升子序列问题时,通常采用自底向上的方法:

  1. 初始化: 创建大小为 n 的数组 b,其中 b[i] 记录了以第 i 个元素为结尾的最长上升子序列的长度。
  2. 动态计算: 对于 i 从 1 到 n:
    • 如果 a[i] > a[j](j < i),则 b[i] = max(b[i], b[j] + 1)
  3. 结果获取: 最长上升子序列的长度为 max(b[1], b[2], ..., b[n])

时间复杂度优化

最长上升子序列问题的基本动态规划算法时间复杂度为 O(n^2),其中 n 是序列的长度。通过使用二分查找技巧,我们可以将时间复杂度优化到 O(n log n):

  1. 初始化: 创建一个大小为 n 的数组 b,其中 b[i] 记录了以第 i 个元素为结尾的最长上升子序列的长度。将 b 初始化为 1。
  2. 动态计算: 对于 i 从 1 到 n:
    • 如果 a[i] > a[j](j < i),则使用二分查找在 b[1:i-1] 中找到满足 b[l] < a[i] 且 b[l+1] >= a[i] 的 l 值。
    • b[i] = max(b[i], l + 1)
  3. 结果获取: 最长上升子序列的长度为 max(b[1], b[2], ..., b[n])

代码示例

以下 Python 代码实现了最长上升子序列问题的优化动态规划算法:

def longest_increasing_subsequence(arr):
  n = len(arr)
  b = [1] * n
  for i in range(1, n):
    l = binary_search(b, 1, i-1, arr[i])
    b[i] = max(b[i], l + 1)
  return max(b)

def binary_search(arr, low, high, key):
  while low <= high:
    mid = (low + high) // 2
    if arr[mid] < key:
      low = mid + 1
    else:
      high = mid - 1
  return low

结论

动态规划法是解决最长上升子序列问题的有效方法。通过采用二分查找技巧优化时间复杂度,该算法可以在更短的时间内高效找到序列中的最长上升子序列。掌握这些技巧将大大提升我们在解决类似优化问题时的效率。