[LeetCode题解] 300. 最长上升子序列

2 篇文章 0 订阅
1 篇文章 0 订阅

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

在Youtube,B站上搜了以下,关于O(nlogn)解法没有找到一讲我秒懂的tutorial,因此详细记录一下LIS 时间复杂度为O(nlogn)的做法。

解法1:DP,通过双层遍历,如果num[i] > num[j],dp[i] = max(dp[j]) + 1 (j from 0 to i)。

class Solution(object):
    def lengthOfLIS(self, nums):
        if not nums:
            return 0
        dp = [0] * len(nums)
        dp[0] = 1
        res = 1
        for i in range(1, len(nums)):
            tmp = 1
            for j in range(i):
                if nums[i] > nums[j]:
                    tmp = max(tmp, dp[j] + 1)
            dp[i] = tmp
            res = max(res, dp[i])
        return res

但是DP解法是有优化空间:
举个例子num = [1, 2, 5, 3, 7],显然dp[2] = 3 (所对应的LIS为[1, 2, 5]),dp[3] = 3 (所对应的LIS为[1, 2, 3])。当i = 4时,由于num[2] < num[3] 并且dp[3] >= dp[2] (此处有贪心思想),所以dp[4]的时候不需要考虑dp[2]。

解法2:贪心 + 二分。

class Solution(object):
    def lengthOfLIS(self, nums):
        if not nums: return 0
        tail = [0] * len(nums)
        size = 0 # tail 实际长度
        for num in nums:
            i, j = 0, size
            while i != j:
                mid = (i + j) // 2
                if tail[mid] < num: i = mid + 1
                else: j = mid
            tail[i] = num
            if i == size: size += 1;
        return size

tail[i]表示的是:nums在长度为i的时候,其LIS的最后元素的最小值。
当tail的有效size为m的时候,m就是我们的答案。(但是请注意, tail[:size]并不一定等同于LIS,仅仅是长度相同)

以nums = [1, 2, 5, 3, 7]为例:
tail[0]:长度为1 -> [1]; [2]; [5]; [3]; [7] -> 1
tail[1]:长度为2 -> [1,2]; [1,5]; [1,3] ,…, [3, 7] -> 2
tail[2]:长度为3 -> [1,2,5], [1,2,3] ,…, [2,5,7] -> 3
我们可以容易得出tail是单调递增的。原因:tail[0] = min(nums),而我们假设tail[1]的连续上升序列为(nums[i], nums[i+1]),显然nums[i] >= min(nums),nums[i+1] > nums[i]。所以tail[1]必然大于tail[0]。

当我们遍历nums的时候。

  1. 如果nums[i]大于tail[size],tail[size + 1] = nums[i]
  2. 如果nums[i]小于tail[size],我们利用二分查找,把大于nums[i]最少的tail[j]替换成nums[i]
    这一步的原因是因为贪心,比如nums = [1,2,8,4]的时候,num = 8的时候, tail[2] = [1, 2, 8]。但是num =
    4的时候,很明显[1,2,4]是要比[1,2,8]更好的。比如nums = [1,2,8,4,7]的时候,LIS是由[1,2,4] + [7]组成的。而由于之前的论证,tail是单调递增,所以我们可以用二分查找,完成替换操作。
    时间复杂度分析:iterate the element of input (n) * binary search (logn) = O(nlogn)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值