📅  最后修改于: 2023-12-03 15:10:37.941000             🧑  作者: Mango
最长递增子序列(Longest Increasing Subsequence,简称 LIS)问题是计算机科学领域中的一个经典问题,也是动态规划领域中的一个经典问题。给定一个长度为n的序列,要求找出一个最长的子序列使得这个子序列的元素是递增的。
最长递增子序列大小 (N log N) 是一种快速求解 LIS 问题的算法,其时间复杂度为 O(N log N)。该算法通过二分法优化寻找最长递增子序列的过程,因此效率较高。
以输入序列 [2, 8, 9, 6, 7, 4]
为例,以下是算法的思路:
将一个长度为 n 的序列转换为长度相等的一个序列 B,其中 B[i] 代表原序列中以 i 结尾的最长上升子序列的长度。在未进行任何比较之前,B 中所有的值设为 1(因为每个元素都可以作为自己的最长上升子序列)。
B: [1, 1, 1, 1, 1, 1]
从第二个元素开始遍历序列,对于每个元素,从序列开头向其前一个元素遍历,如果前面的元素小于当前元素,则更新 B[i] 为前面元素所在的最长上升子序列的长度加 1。
B: [1, 2, 3, 2, 3, 2]
最终 B 中的最大值即为输入序列的最长递增子序列的长度。
通过上面的示例可以看出,该算法的基本思路是动态规划。但是,如果直接用动态规划求解 LIS 问题的时间复杂度是 O(n^2),无法满足较大规模的需求。因此,该算法引入了二分法的思想来减小计算量。具体实现如下:
定义一个数组 tails,talis[i] 表示长度为 i 的递增子序列的末尾元素的最小值。接着遍历输入序列,如果当前元素大于 tails 中的最大值,则将当前元素添加至 tails 末尾;否则,在 tails 中寻找第一个大于等于当前元素的元素并进行替换。
tails: [2, 4, 6, 7, None, None]
可以看到,tails 表示长度为 1 的递增子序列的末尾元素为 2,长度为 2 的递增子序列的末尾元素为 4,以此类推。
tails 中的非 None 元素个数即为输入序列的最长递增子序列的长度。
以下为 Python 代码实现:
def length_of_lis(nums):
tails = [None] * len(nums)
size = 0
for num in nums:
left, right = 0, size
while left < right:
mid = (left + right) // 2
if tails[mid] < num:
left = mid + 1
else:
right = mid
tails[left] = num
size = max(size, left + 1)
return size
最长递增子序列大小 (N log N) 算法通过二分法优化了计算 LIS 问题的过程,其时间复杂度为 O(N log N)。该算法是动态规划的实现思路,但相比传统的动态规划实现方式,它的效率更高、空间消耗更少。