LeetCode 300. 最长递增子序列

链接:https://leetcode.cn/problems/longest-increasing-subsequence
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

思路1:动态规划
设dp为在 i 处,包含nums[i],能够获得的最长递增子序列长度。
dp[i] 需要取 0 - i 中所有dp中的最大值。
也就是 dp[i] = max(dp[i], dp[j] + 1)
而最终的返回结果,也应该是dp这个数组中的最大值。
由于所有状态都要保留,无法优化空间。

var lengthOfLIS = function(nums) {
    let dp = new Array(nums.length).fill(1);
    let result = 1;

    for (let i = 1; i < nums.length; i++) {
        for (let j = 0; j < i; j++) {
            if (nums[j] < nums[i]) {
                dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }
        result = Math.max(result, dp[i]);
    }
    return result;
};

思路2:贪心 + 二分查找
如果一个长度是x的上升子序列,能够找到所有长度为x的上升子序列中结尾最小的值,那么会有更多的可能构造一个长度更长的上升子序列。
设有一个数组tail,它里面存放的是在第 i 个索引处,nums中的长度为 i + 1 的递增子序列的最小结尾值。
例如数组[10, 9, 2, 5, 3, 7, 101, 18] ,长度为2的所有上升子序列中,结尾最小的是3,所以tail[1] = 3。
最终,这个tail数组的长度就是nums数组中最长上升子序列的长度。
对于nums[i] > tail[tail.length - 1],即索引 i 处的值比tail数组中最末的还要大,那么可以直接将其push进tail数组,作为下一个上升子序列中结尾最小的值。
对于nums[i] <= tail[tail.length - 1] 的情况,需要在tail中查找可以让nums[i] 插入的位置,即在tail中找到一个位置 j,使得tail[j - 1] < nums[i] <= tail[j],这里可以用到二分查找。

var lengthOfLIS = function(nums) {
    let tail = [nums[0]];
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] > tail[tail.length - 1]) {
            tail.push(nums[i]);
        } else {
            let left = 0;
            let right = tail.length - 1;
            while (left < right) {
                let middle = left + ( (right - left) >> 1);
                if (tail[middle] < nums[i]) {
                    left = middle + 1;
                } else {
                    right = middle;
                }
            }
            tail[right] = nums[i];
        }
    }
    return tail.length
};

你可能感兴趣的:(leetCode刷题,leetcode,算法,职场和发展)