前言:LIS最长递增子序列问题,是极其有趣的问题,除了最最经典的DP解法外,还有不少解法值得研究
——而,每种解法都能让我们更进一步认识算法的奥妙……
「解题索引:」
「解题索引:」
【1】什么是LIS问题《
- 递增子序列的定义:对任意一个数组,按原数组下标顺序选择一些元素构成一个新序列,且该序列单调递增
- 最长单调子序列:从某数组的所有递增子序列中选择最长的子序列
- 我把LIS问题分成两种,(第一种)是只需求最长递增子序列长度的,(第二种)是求出所有最长递增子序列所构成的集合
- 最长递增子序列,LIS问题解完后 对(严格单调递增非严格单调递增?)有何区别?【||】将递增改为递减又如何处置——解透LIS问题的本质即可!
【2】最经典的第一类LIS问题》
给你一个整数数组 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数组/函数——>状态转移
动态规划的核心设计思想:递推(数学归纳)
分析:在这里,我们定义dp[i]表示为从nums[0]到nums[i]间的最长递增子序列长度。(解决子序列问题的经典套路)
根据这个定义,我们可以推出最简的情况(base case)——dp[i]初始值为 1 ,因为以nums[i]结尾的递增子序列至少包含它自己一个元素。
算法推进的过程是:从i=0开始遍历dp数组,通过dp[0],dp[1]…dp[i-1],来推导dp[i],这里我们发现,dp[i]的状态可以由已知的dp[0…i-1]的状态推知,实现状态转移。现在列出推导过程:
假设dp[0…i-1]已知,很容易知道,只要找到前面结尾比nums[i]小的子序列,然后将nums[i]接到其最后就可以形成一个新的子序列;
即:dp[i]=dp[j]+1,j<i且nums[j]<nums[i];
如果nums[i]前的数均比nums[i]大,那么dp[i]自然就保持初始的 1。
不知不觉,我们已经把状态转移方程列出。
以上图为例:
dp[4]=dp[1]+1,nums[1]<nums[4]
具体的代码实现
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int>dp(nums.size(),1);//初始化dp数组,均为最简情况1️⃣
for(int i=0;i<nums.size();i++){
for(int j=0;j<i;j++){
if(nums[i]>nums[j]) //从0~i-1,遍历,推导dp[i]
dp[i]=max(dp[i],dp[j]+1);//dp[i]从中取最大值即可
}
}
int res=0;
for(int i=0;i<dp.size();i++){
res=max(res,dp[i]); //遍历dp[i],从中取最大的dp[i]
} //即为最长递增子序列的长度
return res;
}
};
两层嵌套循环,时间复杂度为O(n^2)
解法二:二分搜索
(一开始,我根本想不到这样标准的动态规划题还可以用二分搜索解o(▽)q
思路分析
LIS问题,和一种叫做patience game的纸牌游戏有关,还有种排序算法叫耐心排序。
这里直接上代码题解了:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int>top(nums.size(),0);
//初始化牌堆顶
int piles=0;
for(int i=0;i<nums.size();i++){
//要处理的扑克牌
int poker=nums[i];
int left=0,right=piles;
//搜索左边界的二分搜索
while(left<right){
int mid=left+(right-left)/2;
if(top[mid]>poker)right=mid;
else if(top[mid]<poker)left=mid+1;
else if(top[mid]==poker)right=mid;
}
//没找到合适的纸牌,新建一堆
if(left==piles)piles++;
//把该牌放到牌堆顶
top[left]=poker;
}
//牌堆数即为LIS长度
return piles;
}
};
时间复杂度为O(n logn)
最长递增子序列问题
前言:这是最最经典的算法题之一,你几乎可以在任何OJ上发现一模一样的LIS问题。
力扣LIS问题
HOJ-LIS问题
POJ-LIS问题
杭电OJ-LIS问题
基本是一模一样的,除了示例…