最长递增子序列
300. 最长递增子序列
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
思路:
动态规划解法
dp[i]
表示以nums[i]
这个数结尾的最长递增子序列的长度
根据这个定义,我们就可以推出
base case
:dp[i]
的初始值为
1,因为以nums[i]
结尾的最长递增子序列起码要包含自己。
index | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
nums | 1 |
4 | 3 |
4 |
2 |
dp[3] = 3
index | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
nums | 1 |
4 | 3 | 4 | 2 |
dp[4] = 2
递推关系,我们只要找到前面比nums[i]
小的子序列,然后把nums[i]
接到后面,就可以形成一个新的递推增子序列,而且这个新的子序列长度加1
所以可以写出:
1 | class Solution { |
⾄此,这道题就解决了,时间复杂度O(N^2)
,可以看出时间复杂度还很高,我们有没有办法降低。
二分查找法
时间复杂度为O(NlogN)
根据题⽬的意思,我都很难想象这个问题竟然能和⼆分查找扯上关系。其实最⻓递增⼦序列和⼀种叫做patience game 的纸牌游戏有关,甚⾄有⼀种排序⽅法就叫做 patience sorting(耐⼼排序)。
⾸先,给你⼀排扑克牌,我们像遍历数组那样从左到右⼀张⼀张处理这些扑克牌,最终要把这些牌分成若⼲堆。
处理这些扑克牌要遵循以下规则:
只能把点数⼩的牌压到点数⽐它⼤的牌上;如果当前牌点数较⼤没有可以放置的堆,则新建⼀个堆,把这张牌放进去;如果当前牌有多个堆可以选择,则选择最左边的那一堆放置。
⽐如说上述的扑克牌最终会被分成这样 5 堆(我们认为纸牌 A 的牌⾯是最⼤的,纸牌 2 的牌⾯是最⼩的)。
为什么遇到多个可选择堆的时候要放到最左边的堆上呢?因为这样可以保证牌堆顶的牌有序(2, 4, 7, 8,Q)
按照上述规则执⾏,可以算出最⻓递增⼦序列,牌的堆数就是最⻓递增⼦序列的⻓度,证明略。
我们只要把处理扑克牌的过程编程写出来即可。每次处理⼀张扑克牌不是要找⼀个合适的牌堆顶来放吗,牌堆顶部的牌不就是有序吗?
这就能用到二分查找了:==用二分查找来搜索当前牌应放置的位置。==
最长递增子序列的个数就是堆牌的个数。
1 | class Solution { |