300. Longest Increasing Subsequence

300. Longest Increasing Subsequence

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

Example:

Input: [10,9,2,5,3,7,101,18]
Output: 4 
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. 

Note:

There may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.

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

方法1: dynamic programming

思路:

dp(n , 0): 以 i 为结尾的数组中,LIS是多少。那么转移方程就是对于所有在i之前j 位置上的数字,他们如果nums[j] < nums[i], dp[i] = max(dp[i], dp[j] + 1);如果nums[j] >= nums[i], dp[i] = max(dp[i], dp[j])。初始化应该是dp[0] = 1,i.e.在第一位上至少有长度为1的LIS。第二位开始dp。注意此时dp的含义规定了以i为结尾,那么全局最大就不是dp[n - 1],而需要另外维持一个全局最大。为什么一定要以 i 为结尾呢?否则仅判断nums[j] < nums[i]无法保证 i 可以续接在 j 所记录的那个LIS上。请体会test case。

易错点

  1. dp的定义以及return值
  2. 初始化
  3. 每次要和当前位的dp最大值比较,i.e. max(dp[i], dp[j] + 1),因为前面可能存在众多小于nums[i]的数,要取最大
  4. 因为3,result每轮i更新一次就行,放在最内层太慢
// [1,3,6,7,9,4,10,5,6]
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int result = 1;
        if (nums.size() == 0) return 0;
        int n = nums.size();
        vector<int> dp(n, 1);
        for (int i = 1; i < nums.size(); i++){
            for (int j = 0; j < i; j++){
                if (nums[j] < nums[i]){
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
            result = max(result, dp[i]);
        }
        return result;
    }
};

方法2:

官解:https://leetcode.com/problems/longest-increasing-subsequence/solution/
Tushar: https://www.youtube.com/watch?v=S9oUiVYEq7E
grandyang: http://www.cnblogs.com/grandyang/p/4938187.html (看讨论)

思路:

建立一个dp,但是不用初始化它的长度,而是随着遍历nums动态的推入元素。每当遍历到nums[i], 我们用binary search查找到它在当前dp数组中应在存在的位置(dp是一个递增的数组),如果比所有元素都大,直接推入dp,使dp长度++;而如果它并没有大于末位元素,我们将用它替代第一个大于等于它的元素。这样在循环终止的时候dp长度,即为我们能获取的LIS。注意这个时候返回的dp可能并不是一个真实存在的IS,但是它的长度却反应了真正LIS。举个例子:

input: [0, 8, 4, 12, 2]

dp: [0]

dp: [0, 8]

dp: [0, 4]

dp: [0, 4, 12]

dp: [0 , 2, 12] which is not the longest increasing subsequence, but length of dp array results in length of Longest Increasing Subsequence.

那么这个dp到底在维护什么?可以这样理解:dp[i]维护的是长度为i+1的递增子序列可以取到的最小结尾值。当替换前面的元素时,会将一个位置上的值尽可能缩小,虽然替换可能破坏了原有的LIS,但是却保留了获得更长LIS的可能性。比如说上面的栗子,遍历到8的时候我们以为长度为2的序列必须以8结尾,但是到4的时候我们就可以选择以4结尾。而之后能够获得的LIS如果可以和[0, 8]续接那么一定可以和[0, 4]续接。2最后替换了4是因为如果后面还有数字,可能以同样的原理接在[0, 2]后面。为什么长度一定是LIS的正确长度呢?因为每次增加dp长度的条件是nums[i]大于之前所有数,这个新的LIS是真实存在的,不管之后会不会被替换。多试几个栗子体会。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> dp;
        for (int i = 0; i < nums.size(); i++){
            int left = 0, right = dp.size();
            while (left < right){
                int mid = left + (right - left) / 2;
                if (dp[mid] < nums[i]) left = mid + 1;
                else right = mid;
            }
            if (left >= dp.size()) dp.push_back(nums[i]);
            else dp[left] = nums[i];
        }
        return dp.size();
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值