Leecode 300. 最长上升子序列

简介: Leecode 300. 最长上升子序列

题目描述

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]

输出: 4

解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

说明:

可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。

你算法的时间复杂度应该为 O(n2) 。

进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

贪心 + 二分查找

思路与算法

考虑一个简单的贪心,如果我们要使上升子序列尽可能的长,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小。

基于上面的贪心思路,我们维护一个数组 d[i],表示长度为 i的最长上升子序列的末尾元素的最小值,用 len 记录目前最长上升子序列的长度,起始时 len 为 1,d[1]=nums[0]。

同时我们可以注意到 d[i]是关于 i 单调递增的。因为如果 d[j]≥d[i] 且 j < i,我们考虑从长度为 ii的最长上升子序列的末尾删除 i-j个元素,那么这个序列长度变为 j ,且第 j个元素 x(末尾元素)必然小于 d[i],也就小于 d[j]。那么我们就找到了一个长度为 j 的最长上升子序列,并且末尾元素比 d[j] 小,从而产生了矛盾。因此数组 d[] 的单调性得证。

我们依次遍历数组 nums[] 中的每个元素,并更新数组 d[] 和 len 的值。如果 nums[i]>d[len] 则更新 len=len+1,否则在 d[1…len]中找满足 d[i−1]<nums[j]<d[i] 的下标 i,并更新 d[i]=nums[j]。

根据 d数组的单调性,我们可以使用二分查找寻找下标 i,优化时间复杂度。

最后整个算法流程为:

设当前已求出的最长上升子序列的长度为len(初始时为 1),从前往后遍历数组nums,在遍历到 nums[i] 时:

如果nums[i]>d[len] ,则直接加入到 d 数组末尾,并更新 len=len+1;

否则,在 dd 数组中二分查找,找到第一个比 nums[i] 小的数 d[k] ,并更新 d[k+1]=nums[i]。

以输入序列 [0, 8, 4, 12, 2][0,8,4,12,2] 为例:

第一步插入 0,d = [0];

第二步插入 8,d = [0, 8];

第三步插入 4,d = [0, 4];

第四步插入 12,d = [0, 4, 12];

第五步插入 2,d=[0,2,12]。

最终得到最大递增子序列长度为 3。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = 1, n = (int)nums.size();
        if (n == 0) return 0;
        vector<int> d(n + 1, 0);
        d[len] = nums[0];
        for (int i = 1; i < n; ++i) {
            if (nums[i] > d[len]) d[++len] = nums[i];
            else{
                int l = 1, r = len, pos = 0; // 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
                //此时使用二分查找的算法
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (d[mid] < nums[i]) {
                        pos = mid;
                        l = mid + 1;
                    }
                    else r = mid - 1;
                }
                d[pos + 1] = nums[i];
            }
        }
        return len;
    }
};


相关文章
【动态规划刷题 15】最长定差子序列&& 最长的斐波那契子序列的长度
【动态规划刷题 15】最长定差子序列&& 最长的斐波那契子序列的长度
|
2月前
acwing 895 最长上升子序列1
acwing 895 最长上升子序列1
30 3
|
2月前
acwing 896 最长上升子序列II
acwing 896 最长上升子序列II
27 2
|
2月前
合唱队行 (最长上升子序列)
合唱队行 (最长上升子序列)
25 0
|
算法 JavaScript Go
【动态规划】最长递增子序列
【动态规划】最长递增子序列
|
算法 程序员 C#
C++二分查找算法的应用:最长递增子序列
C++二分查找算法的应用:最长递增子序列
|
算法
【学会动态规划】 最长递增子序列(26)
【学会动态规划】 最长递增子序列(26)
47 0
|
存储 安全 测试技术
蓝桥<排水管道>题的题解(最长上升子序列的变式)
蓝桥杯排水管道AC题解,帮助你加深理解LIS最长上升子序列
蓝桥<排水管道>题的题解(最长上升子序列的变式)
(区间dp最长上升子序列,最长下降子序列)
(区间dp最长上升子序列,最长下降子序列)
111 0
|
算法
leetcode-每日一题873. 最长的斐波那契子序列的长度(哈希和二分)
题目要求斐波那契数列长度要大于等于3,就等于说要确定 x[1] 和 x[2]来确定x[3]…x[n]之和的数列,所以我们用两层for循环来枚举x[1] 和 x[2] ,因为斐波那契数列满足 x[i] = x[i - 1] + x[i - 2], 所以x[3] = x[1] + x[2] x[4] = x[3] + x[2]…,我们只需要三个变量来不断变换, 每次从 arr 中找前两个数,然后查看后续在符合斐波那契的数在arr中是否存在
110 0
leetcode-每日一题873. 最长的斐波那契子序列的长度(哈希和二分)