给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶:
你能将算法的时间复杂度降低到 O(n log n) 吗?
思路:维护一个tail数组,tail[k]表示长度为k+1的最长上升子序列的尾部元素例如:原数组为[10,9,2,5,3,7,21,18]
tail每轮更新情况:
i=0时:tails:[10]
i=1时:tails:[9]
i=2时:tails:[2]
i=3时:tails:[2,5]
i=4时:tails:[2,3]
i=5时:tails:[2,3,7]
i=6时:tails:[2,3,7,21]
i=7时:tails:[2,3,7,18]
每次都保持尾部元素最小。
如果再加一个5元素,则通过二分法找到第一个大于5的元素7,将7替换成5。
如果再加一个9元素,则通过二分法找到第一个大于9的元素18,将18替换成9。如此下去。
因为题目要求将算法复杂度降到nlogn,而遍历数组中的数字是免不了的,只能优化tail的更新过程,我们可以看到tails中的元素必然是有序的,所以显然我们可以通过二分查找快速定位到需要替换的元素位置。代码如下:
int lengthOfLIS(vector<int>& nums) { vector<int> tails(nums.size()); int res = 0; for(int num : nums) { int i = 0, j = res; //while循环退出条件:i == j while(i < j) { int mid = (i + j) / 2; if(num > tails[mid]) i = mid + 1; else j = mid; //此处不能是mid-1 } tails[i] = num; //res == j表示j一直没有前移,一直指向最末尾,表示tail中所有数字都小于num,直接加到tails末尾 if(res == j) res++; } return res; }