ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

力扣-300-最长递增子序列

2022-08-07 17:02:01  阅读:128  来源: 互联网

标签:nums 300 递增 len 力扣 int length 数组 dp


直达链接

想到了连续子数组的最大和

自己想

我本来想倒着推,有点像mari和shiny,但是不对

class Solution {
public:
	int lengthOfLIS(vector<int>& nums) {

		int length = nums.size();
		if (length < 2) return 1;

		vector<int> dp(length);
		dp[length - 1] = 1;
		nums[length - 2] < nums[length - 1] ? dp[length - 2] = 2 : dp[length - 2] = 1;


		for (int i = length-2; i > 0; --i) {
			if (nums[i - 1] < nums[i]) {
				dp[i - 1] = dp[i] + 1;
			}
			else {
				dp[i - 1] = dp[i];
			}
		}
		return dp[0];
	}
};

因为没法很好地解决这两个用例:

	 // vector<int> nums = { 0,1,0,3,2,3 };
	 vector<int> nums = { 4,10,4,3,8,9 };

官方题解

1. 动态规划

数组dp[i]表示nums中直到i为止(包括nums[i])的子数组中,最长子序列的长度

状态转移方程为:dp[i] = max(dp[j])+1,j<i且nums[j]<nums[i]


class Solution {
public:
	int lengthOfLIS(vector<int>& nums) {

		int n = nums.size();

		// 由题,n>1,所以省略检查
		
		vector<int> dp(n, 0);

		// 遍历dp数组,每一次都将数组元素初始化为1
		for (int i = 0; i < n; ++i) {
			dp[i] = 1;

			for (int j = 0; j < i; ++j) {
				// 遍历比i小的dp数组元素,依次比较取大值
				// 以0为例:0不会进入循环
				// 以1为例:dp[1]=max(dp[1],dp[0])
				// 以2为例:dp[2]=max(dp[2],dp[0]),dp[2]=max(dp[2],dp[1])
				if (nums[j] < nums[i]) {
					// 这里的dp[i]并不总等于1,在前面的二层循环中可能被刷新
					// 因为j<i,所以dp[j]的值一定是知道的
					dp[i] = max(dp[i], dp[j] + 1);
				}
			}
		}
		// 没见过的新函数,用于求STL容器中的最大值
		return *max_element(dp.begin(), dp.end());
	}
};

2. 贪心+二分查找

(反正我是没想到二分查找在这里怎么用的)
贪心思想是:如果要想让子序列尽量长,就需要让序列元素的“上升”(数值增大)尽量平缓

关键在于一个数组d[i],表示长度为i的子序列的末尾值(即当前序列最大值)的最小值

然后可以用反证法证明数组d[]是一个单调递增数组

那么意义有二:1. 可以用二分法2. 数组d[]就是最长递增子序列

以输入序列 [0, 8, 4, 12, 2] 为例:
初始化,len=1,d[1] = nums[0] = 0

第二步插入 8,nums[1] = 8 > d[1] = 0,len++=2,d[2] = 8,d = [0, 8]

第三步插入 4,nums[2] = 4 < d[len] = d[2] = 8,
去找第一个比4小的位置,找到1,即d[1] = 0,
更新d[1+1]=d[2] = 4,d = [0, 4]
len不变仍然为2

第四步插入 12,12>d[2] =4,len++=3,d[3] = 12,d = [0, 4, 12]

第五步插入 2,2<d[3] = 12,
去找第一个比2小的位置,d[1] = 0
更新d[1+1] = d[2] = 4 = 2
d = [0, 2, 12]

遍历整个nums数组,遇到比d数组最后一个(最大一个)元素大的,就追加到d数组后面,并更新最大长度len的值
遇到小的,就(通过二分查找在d数组中)找第一个比它还小的,更新这个元素后一个位置的值

class Solution {
public:
	int lengthOfLIS(vector<int>& nums) {

		int len = 1;// 代表当前最长递增子序列长度
		int n = nums.size();

		// 省略检查

		vector<int> d(n + 1, 0);// 为什么长度是n+1?d[0]没有用,长度为0的子序列没有意义

		d[len] = nums[0];// 初始化d[1]

		// 怎么不考虑d[n]吗?整个nums都是递增的呢?子序列是否包含自身?
		// 主要是nums[n]非法吗?
		for (int i = 1; i < n; ++i) {
			// 遍历元素
			if (nums[i] > d[len]) {
				// 这里更新了len自增
				d[++len] = nums[i];
			}
			else {

				// 如果小于

				int left = 1, right = len, pos = 0;
				// 如果找不到说明所有数都比nums[i]大,此时需要更新d[1],所以将pos设为0
				
				// 二分开始,找第一个比nums[i]小的位置
				while (left <= right) {
					int mid = (left + right) >> 1;

					// 返回第一个小于
					if (d[mid] < nums[i]) {
						pos = mid;
						left = mid + 1;
					}
					else {
						right = mid - 1;
					}

					d[pos + 1] = nums[i];
				}
			}
			return len;
		}
	}
};

标签:nums,300,递增,len,力扣,int,length,数组,dp
来源: https://www.cnblogs.com/yaocy/p/16554375.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有