leetcode-300. 最长上升子序列

题目

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

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

解题思路

dp

dp[i]表示以nums[i]结尾的最长上升子序列长度,更新规则为:
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) ,      if      n u m s [ i ] > n u m s [ j ] , ∀ j < i dp[i] = max(dp[i], dp[j]+1), \;\; \text{if}\;\; nums[i]>nums[j], \forall j < i dp[i]=max(dp[i],dp[j]+1),ifnums[i]>nums[j],j<i
初始时dp[0]=1

时间复杂度: o ( n 2 ) o(n^2) o(n2)
空间复杂度: o ( n ) o(n) o(n)

贪心 + 二分

参考:https://leetcode.com/problems/longest-increasing-subsequence/solutions/1326308/c-python-dp-binary-search-bit-segment-tree-solutions-picture-explain-o-nlogn/?orderBy=most_votes

看了好几篇题解都没看懂,终于找到一篇写得很清楚的了……

nums = [2, 6, 8, 3, 4, 5, 1]为例,来找出所有上升子序列

  1. 选第1个元素,有sub1 = [2]
  2. 选第2个元素,有sub1 = [2,6]
  3. 选第3个元素,有sub1 = [2,6,8]
  4. 碰到3,不能往sub1里加了,但是想留下来3,因为越小的数字,对我们找到长的上升子序列越有利,此时我们再开一个数组保存当前结果,也即此时有:sub1 = [2,6,8], sub2 = [2,3]
  5. 碰到4,不能往sub1里加,但是可以往sub2里加,此时有:sub1=[2,6,8], sub2=[2,3,4]
  6. 碰到5,同理加到sub2里,此时有:sub1=[2,6,8], sub2=[2,3,4,5]
  7. 碰到1,不能加到sub1, sub2里,因此再开一个,此时有:sub1=[2,6,8], sub2=[2,3,4,5], sub3=[1]
  8. 最后求结果,从sub中选择最长的那个,res = len(sub2) = 4

思路是这么个思路,但是发现要消耗比较多空间来保存sub数组,不太划算,所以考虑合并这些数组。当碰到新的数字时,可以直接替换已有的数组中的数字。如上面第4步,可以直接把3插入到sub1里,替换掉6。这种方法不会影响sub1的长度,因此假设最终sub1仍然为最长数组,那么长度没有变化,len(sub1)仍然是正确答案。同时,这种方法可以保留新的小一些的数字,为之后更新数组做贡献,比如按此方法替换掉6后,在第5步时,又会用4替换掉8,此时sub1完全被sub2取代

替换数字时,因为sub数组是递增的,所以可以用二分查找,找到当前数字能够插入的最左索引即可

时间复杂度: o ( n log ⁡ n ) o(n\log n) o(nlogn)
空间复杂度: o ( n ) o(n) o(n)

代码

dp

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        dp = [1] * len(nums)
        for i in range(1, len(nums)):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j] + 1)
        return max(dp)

贪心+二分

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        aux_stack = [nums[0]]
        for each_num in nums[1:]:
            if each_num > aux_stack[-1]:
                aux_stack.append(each_num)
            else:
                index = bisect.bisect_left(aux_stack, each_num)
                aux_stack[index] = each_num
        return len(aux_stack)

你可能感兴趣的:(OJ题目记录,leetcode,算法,贪心算法)