leetcode 300 Longest Increasing Subsequence 最长上升子序列 动态规划

leetcode 300 Longest Increasing Subsequence 最长上升子序列

leetcode 2020年3月 每日一题打卡
据说华为问过这道题

问题:
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明: 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

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

知识: 动态规划

适合使用动规求解的问题:

  1. 问题具有最优子结构。
  2. 无后效性,多指求最优解问题。

动规解题思路:

  1. 划分子问题
  2. 确定状态
  3. 确定状态转移方程
  4. 确定起始条件或边界条件

递归变动规,就是将含有n个参数的递归函数,变为一个n维数组,存储的值等于递归函数的返回值。

思路: python 动态规划,child[i] 表示以nums里第i个元素结尾的序列里最长上升子序列的长度。子问题:求解前i个元素里最长上升子序列的长度。状态:child[i]。状态转移方程:每个子问题中,遍历所有先前元素nums[j],如果nums[j]<nums[i],则child[i]等于所有child[j]+1中的最大值。边界状态:起始值为1。时间复杂度:O(n^2) , 动态规划的状态数为 n,计算状态时,需要 O(n) 的时间遍历之前的状态,所以总时间复杂度为 O(n^2)。空间复杂度:O(n),因为需要额外使用长度为 n的数组。

细节:

  1. 排除nums为空的情况
  2. best_i起始值设为1而不能为0

代码:

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # 动态规划 O(N^2)
        child=[] # child[i]表示以nums里第i个元素结尾的序列里最长上升子序列的长度
        if nums == []:
            return 0
        child.append(1)
        for i in range(1,len(nums)):
            best_i=1
            for j in range(0,i):
                if nums[i]>nums[j]:
                    tem=child[j]+1
                    if tem>best_i:
                        best_i=tem
            child.append(best_i)
        return max(child)

方法2: 贪心+二分查找:看官方题解。贪心算法的核心思想是寻找局部最优。时间复杂度 O(NlogN)。这道题只输出长度,所以可以用此算法代替,如要输出序列,则不能如此。

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # 贪心+二分查找 O(NlogN)
        if nums == []:
            return 0
        tail=[nums[0]] # 存长度最长上升子序列的最后一个元素值
        len=1 # 最长上升子序列的长度
        
        for num in nums[1:]:
            if num>tail[len-1]:
                tail.append(num)
                len+=1
            else:
                # 二分查找比num大的最小的元素及位置
                l=0
                r=len-1
                while l<r:
                    mid = int((l+r)/2)
                    if tail[mid] == num:
                        l=mid
                        break
                    if tail[mid]<num:
                        l=mid+1
                        continue
                    elif tail[mid]>num:
                        r=mid
                        continue
                tail[l]=num
            
        return len

ps. 第二种方法不典型,着重理解第一种。
本博客为原创作品,欢迎指导,转载请说明出处,附上本文链接,谢谢

发布了20 篇原创文章 · 获赞 1 · 访问量 206

猜你喜欢

转载自blog.csdn.net/weixin_43973433/article/details/104856044