Leetcode300题 最长递增子序列详解

文章介绍了如何解决LeetCode第300题,通过结合动态规划和二分查找算法,找到整数数组中的最长递增子序列,以nlogn的时间复杂度实现。关键在于利用子序列的单调性和二分查找更新序列元素,使序列上升速度尽可能慢。
摘要由CSDN通过智能技术生成

LeetCode 300题,最长递增子序列详解

给你一个整数数组nums,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如[3,4,2,7]是数组[0,3,1,6,2,2,7]的子序列。

子序列是顺序不可以改变,但是可以不连续的。子数组必须得是连续的。
原题目要求最长递增子序列的长度,如果只使用动态规划的话,时间复杂度会是n²,不符合。因此我们需要将动态规划与二分结合。
二分的时间复杂度是logn,结合后的时间复杂度是nlogn。

思想:最长子序列是上升最慢的,我们需要让序列上升的尽可能的慢。使用反证法站名末尾数组最小值的单调性,因此可以使用二分法来查找更新元素的最小值,找到上升最慢的序列。

vector<int> lengthOfLIS(vector<int>& nums){
	vector<int> ans;//组里用来存放一些递增子序列
	vector<int> maxLen;//存放最长自增子序列,其实就是贪心+二分的dp数组
	
	ans.push_back(nums[0]);//递增子序列一开始存在的元素是nums[0]
	maxLen.push_back(1);//初始时最长递增子序列长度为1
		
	for(int i = 1;i<nums.size();i++){
		if(nums[i]>ans.back())//单调递增{
			ans.push_back(nums[i]);//和初始化的逻辑差不多,就不断的增加最长递增子序列的长度
			maxLen.push_back(ans.size());//同上
		}
		else{
			auto pos = lower_bound(ans.begin(),ans.end(),nums[i])-ans.begin();//第一个大于等于nums[i]的位置。
			ans[pos] = nums[i];//将pos处的值换成nums[i],pos处位置元素肯定是比nums[i]大的,这样换小了。
			maxLen.push_back(pos+1);//长度更改为pos+1,因为0处位置也很重要,所以说加上0.
		}
	}

	//return *max_element(maxLen.begin(),maxLen.end());这个其实就是上面leetcode的要求
	//填充最长递增子序列  返回最长递增子序列
	int i = nums.size()-1;//i对应于nums
	int j = ans.size();//j对应于ans
	while(j>0)
	{
		if(maxLen[i]==j)
		{
			ans[--j] = nums[i];	
		}
		i--;
	}
	return ans;
}

lower_bound(begin,end,val)包含在中,它的作用是返回有序数组begin,end中第一个大于等于val的元素的迭代器。
使得最长上升递增子序列的元素变得更加小,这里肯定是nums[i]的值更加小。

关键思想其实就是尽可能的使数据上升的更加慢,在ans内存储的递增子序列,如果新来的数字是递增的,那样的话直接加在后面就可以了。要不然的话需要找到当前元素的lower_bound的位置,之后需要在当前位置加上该位置的值,ans[pos] = nums[i],同时maxLen.push_back(pos+1)。具体看备注。

需要返回最长递增子序列的值,就需要往ans里面加入元素,j是ans里面大小的位置。i是maxLen的位置下标。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值