LIS
面试经典题:最长递增子序列
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
1 | 输入:nums = [10,9,2,5,3,7,101,18] |
示例 2:
1 | 输入:nums = [0,1,0,3,2,3] |
示例 3:
1 | 输入:nums = [7,7,7,7,7,7,7] |
1 <= nums.length <= 2500-104 <= nums[i] <= 104
让我们用一个具体的例子来模拟最长递增子序列(LIS)的 O(nlogn) 时间复杂度的 C++ 实现。
模拟步骤
- 初始化:
tail数组用于存储当前 LIS 的末尾元素。length变量用于记录当前 LIS 的长度。
- 遍历数组:
- 对于每个元素
nums[i],根据其值与tail数组中的元素进行比较:- 如果
nums[i]小于tail[0],则替换tail[0]。 - 如果
nums[i]大于tail[length - 1],则将其添加到tail数组的末尾,并增加length。 - 否则,使用二分查找找到
tail数组中第一个大于等于nums[i]的元素,并替换它。
- 如果
- 对于每个元素
具体模拟
假设我们有以下数组:
1 | vector<int> nums = {2, 5, 3, 7, 11, 8, 10, 13, 6}; |
- 初始状态:
tail = [0, 0, 0, 0, 0, 0, 0, 0, 0]length = 1
- 处理第一个元素
2:tail = [2, 0, 0, 0, 0, 0, 0, 0, 0]length = 1
- 处理第二个元素
5:5 > tail[0],所以tail[1] = 5tail = [2, 5, 0, 0, 0, 0, 0, 0, 0]length = 2
- 处理第三个元素
3:3在tail[0]和tail[1]之间,二分查找找到位置1,替换tail[1]tail = [2, 3, 0, 0, 0, 0, 0, 0, 0]length = 2
- 处理第四个元素
7:7 > tail[1],所以tail[2] = 7tail = [2, 3, 7, 0, 0, 0, 0, 0, 0]length = 3
- 处理第五个元素
11:11 > tail[2],所以tail[3] = 11tail = [2, 3, 7, 11, 0, 0, 0, 0, 0]length = 4
- 处理第六个元素
8:8在tail[2]和tail[3]之间,二分查找找到位置3,替换tail[3]tail = [2, 3, 7, 8, 0, 0, 0, 0, 0]length = 4
- 处理第七个元素
10:10 > tail[3],所以tail[4] = 10tail = [2, 3, 7, 8, 10, 0, 0, 0, 0]length = 5
- 处理第八个元素
13:13 > tail[4],所以tail[5] = 13tail = [2, 3, 7, 8, 10, 13, 0, 0, 0]length = 6
- 处理第九个元素
6:6在tail[1]和tail[2]之间,二分查找找到位置2,替换tail[2]tail = [2, 3, 6, 8, 10, 13, 0, 0, 0]length = 6
最终,tail 数组的内容为 [2, 3, 6, 8, 10, 13, 0, 0, 0],length 为 6。因此,最长递增子序列的长度为 6。
O(nlog(n))时间复杂度的代码
1 |
|
评论

