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

2022/8/7 23:28:31

本文主要是介绍力扣-300-最长递增子序列,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

直达链接

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

自己想

我本来想倒着推,有点像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;
		}
	}
};


这篇关于力扣-300-最长递增子序列的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程