300. 最长上升子序列

题目描述:
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:

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

说明:

可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

思考:
子序列和子串是不同的,子串一定的连续的,子序列不一定是连续的。

方法一

思路1:动态规划(时间复杂度O( N 2 N^{2} N2))
代码实现:

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        length = len(nums)
        if length == 0:
            return 0
        dp = [1] * length    # 先都赋值为1,因为最小子序列长度为1
        for i in range(length):
            for j in range(i):
                if nums[j] < nums[i]:
                    dp[i] = max(dp[i],dp[j] + 1)
        return max(dp)       # 返回dp中的最大值,即最大子序列的长度

方法二

思路2:二分查找(时间复杂度O( N l o g N NlogN NlogN))
最长递增子序列与patience game的纸牌游戏有关,有一种排序方法叫耐心排序。思想如下:
一排扑克牌,遍历时根据一定规则把扑克牌分成若干堆。规则是:

  1. 只能把点数小的排压到点数大的牌上。(牌数一样大也可以)
  2. 如果大的牌没有可放置的堆,就开辟新的堆。
  3. 如果一张牌有多个可选择放置的堆,就放在最左面的堆上(这样可以保证堆顶有序)。

例如:(假如A最大)
在这里插入图片描述

牌的堆数就是我们想求的最长递增子序列的长度。
在这里插入图片描述
所以该问题就转换为找合适的位置放置纸牌,因为堆顶有序(从小到大),所以可以用二分查找。
代码实现:

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        length = len(nums)
        if length == 0:
            return 0
        top = [0]*length       # 堆顶元素,即每个堆的最小值,因为只有小牌只能放到打牌上面
        sumnum = 0             # 堆的个数
        for poker in nums:
            left = 0           # 第一个堆
            right = sumnum     # 最后一个堆
            while left < right:
                mid = (left + right) // 2
                if poker <= top[mid]:   # 如果牌数小于等于中间堆牌数,则当前中间堆可以放置该牌,但是因为规则是放在最左堆,所以需要去左半部分继续寻找
                    right = mid
                else:                   # 如果牌数大于中间堆,则当前中间堆不能放置该牌,需要去右半部分继续寻找
                    left = mid + 1
            if left == sumnum:  # 没找到合适的位置放置
                sumnum += 1     # 新建一个堆
            top[left] = poker   # 更新每个堆的最小值
        return sumnum
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值