300. Longest Increasing Subsequence

本文介绍了解决LeetCode上的最长上升子序列问题的两种方法:一种是动态规划方法,时间复杂度为O(n^2),另一种是使用二分查找优化的方法,时间复杂度降低到了O(n log n)。

题目:Longest Increasing Subsequence

原题链接:https://leetcode.com/problems/longest-increasing-subsequence/

Given an unsorted array of integers, find the length of longest increasing subsequence.

For example,
Given [10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.

Your algorithm should run in O(n * n) complexity.

Follow up: Could you improve it to O(n log n) time complexity?

给出一个未排序的整数序列,返回其中最长上升子序列(LIS)的长度。
例:[10, 9, 2, 5, 3, 7, 101, 18]
那么LIS为[2, 3, 7, 101],所以长度为4.
注意:算法要求时间复杂度为O(n * n) ,如果能做到O(n log n)更好。

O(n * n)复杂度的解决方法:
设数组的长度为len,设dp[ len ],dp[ i ]表示以nums[ i ]结尾的最长子序列的长度,初始都设为1。
那么dp[ 0 ] = 1,对于其他任意dp[ i ],dp[ i ]的值为dp[ 0 … i - 1 ]中所有结尾值小于nums[ i ]的最大dp值加1。即如果nums[ i ] > nums[ j ] , 则令dp[ i ] = max( dp[ j ] + 1, dp[ i ] ),扫描数组结束然后返回最大的dp值即可。
代码如下:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = nums.size();
        if(!len) return 0;
        int dp[len], ans = 1;
        fill(dp, dp + len, 1);
        for(int i = 1; i < len; ++i) {
            for(int j = 0; j < i; ++j) {
                if(nums[i] > nums[j]) {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            if(dp[i] > ans) ans = dp[i];
        }
        return ans;
    }
};

O(n log n)复杂度的解决方法:
设一个数组ans,ans的长度表示遍历到当前元素为止的LIS的长度。
扫描nums,每到一个元素num,在ans中寻找不小于num的第一个元素,如果没有寻找到,则往ans中插入这个元素,如果找到了,则把这个元素替换成num。
以样例[10, 9, 2, 5, 3, 7, 101, 18]为例,模拟每一次遍历的结果:

当前遍历元素ans中的元素ans的长度
10101
991
221
52,52
32,32
72,3,73
1012,3,7,1014
182,3,7,184

注意,虽然长度是LIS的长度,但是ans序列中的元素却并不能保证一定就是LIS,只能保证最后一个元素是LIS中的最后一个元素。

代码如下:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> ans;
        for(int num : nums) {
            auto it = lower_bound(ans.begin(), ans.end(), num);
            if(it == ans.end()) ans.push_back(num);
            else *it = num;
        }
        return ans.size();
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值