最长单调递增子序列_每日算法LeetCode300:最长递增子序列(难度系数1/5)

导读:算法哥发现把题目难度降低后,阅读量显著上升,所以今天再普及一道动态规划入门题。

题目描述:

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

示例:

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

说明:

  • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
  • 你算法的时间复杂度应该为 O(n2) 。

进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?


题目分析:

O(n^2)算法相信大家都会就不废话了,动态规划!!!定义dp[i]表示已第i个数字结尾的最长递增子序列的长度,则:

dp[i] = max(dp[j] + 1, 0 <= j < i && nums[j] 

通估测试的源码如下:

d7bc793250d377cadeab30450f832aea.png

O(n^2)算法

那么怎么把O(n^2)算法优化到O(nlogn)呢?

依然是动态规划,不过这一次,dp[i]的状态需要重新设计一下,

dp[i]表示最长递增子序列长度为i+1时(因为数组下标从0开始的),末尾最小的数

有点绕,先不管,举个例子,你就明白鸟~

假设有数组2,4,3,4,我们依次处理,

首先考虑第一个元素2,此时dp数组里为空,所以直接插入dp数组,dp[0] = 2,表示当前最长递增子序列长度为1时,子序列末尾最小的数是2,此时只有一个数字,当然是2;
下一个数字4,此时我们在dp数组里找到一个位置,使得位置前一个数比4小,后一个数比4大,此时dp里只有一个2,所以找到2的末尾,所以直接插入4,此时dp[1]=4,表示当前最长递增子序列长度为2的时候,末尾最小元素是4;
接下来考虑3,此时依然是在dp数组里找到一个位置,使得位置前一个数比3小,后一个数比3大,所以3应该插入dp数组里4的位置,dp[1]=3,表示当前最长递增子序列长度为2时,末尾最小的元素是3,为什么要把4覆盖掉呢 ?原因很简单,因为2,3比2,4更容易形成更长的递增子序列;
最后在考虑4,此时,4比dp数组里最末尾的3大,所以直接放到dp数组尾部即可,即dp[2]=4;

最后,最长递增子序列的长度就是dp数组里元素的个数!!!

我们每次考虑一个元素时,都是找到一个位置,使得前一个数比它小,后一个比它大,然后覆盖后一个元素(假设这个元素下标是i),因为我们找到了一个数字,使得最长递增子序列长度不变时(长度i+1),最后一个元素更小了,这样更利于形成更长的最长递增子序列!

显然,dp数组是递增的(每次插入都保证了比前面大,比后面小),而每次查找的过程不就是二分查找吗 ?所以复杂度是O(nlogn)!!!虽然算法哥,每次都是说显然!显然!显然!但是对于初学者,理解起来会有点困难,自己在纸上画画,体会下,哈哈哈

b09ae25a69a9e48b8238a5cac407d179.png

O(nlogn)

有个偷懒的方法,二分查找直接使用C++的lower_bound干净利落的搞定了!C++真是算法比赛里的大杀器!

总结:

两种方法都可以看到动态规划的影子,永远记住算法哥对动态规划问题的总结:

大问题可以转化成小问题,反过来,小问题可以推导出大问题!!!
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值