题一.摆动序列(LeetCode)
题目描述
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
例如,
[1, 7, 4, 9, 2, 5]
是一个 摆动序列 ,因为差值(6, -3, 5, -7, 3)
是正负交替出现的。相反,
[1, 4, 7, 2, 5]
和[1, 7, 4, 5, 5]
不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。给你一个整数数组
nums
,返回nums
中作为 摆动序列 的 最长子序列的长度 。
示例 1:
输入:nums = [1,7,4,9,2,5]
输出:6
解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。示例 2:
输入:nums = [1,17,5,10,13,15,10,5,16,8]
输出:7
解释:这个序列包含几个长度为 7 摆动序列。
其中一个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8)。示例 3:
输入:nums = [1,2,3,4,5,6,7,8,9]
输出:2
题目分析
因为一段相邻的相同的相同元素中我们最多只能选择其中的一个,所以我们可以忽略相邻的相同元素。现在我们可以假定序列中任意两个相邻元素都不相同,即要么左边大于右边,要么右边大于左边。
利用示例2分析,解释中长度为7的序列有 [1, 17, 10, 13, 10, 16, 8] 。对应着下图可以发现,拐点或者极值点许多在示例的序列中。自然想到,将非极值点的替换成极值点 [1, 17, 5, 15, 5, 16, 8],可以发现仍是摆动序列,且摆动幅度更大。
由此,我们可以考虑采用贪心策略—在上升和下降的某一段中尽量的选择最大的和最小的。因为,其中的过度元素可以换成邻近的极值点。
如何转化成代码呢?我们可以考虑设两个值left 和 right。left的值可以通过循环中将上一个循环中的right赋值给下一个循环中的left。
left=nums[i]-nums[i-1]
right=nums[i+1]-nums[i]
还需要考虑一下中间出现无波动的情况,即连续几个值相等。
考虑到重复的值对摆动序列的长度没有影响,则可以直接忽略。即,若是其差值为0,则continue跳转到下一次循环。
考虑一下初始化问题,对于 i = 0的情况,除非nums[0] = nums[1] = ...,别的时候nums[0]都可以作为序列的一元,即ret=1。可以将left赋值为0,则在第一个循环的时候,通过条件判断对ret赋值。
题解
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
int left = 0, right = 0;
int n = nums.size();
if (n < 2) return n;
int ret = 0;
for (int i = 0; i < n - 1; i++)
{
right = nums[i + 1] - nums[i];
if (right == 0) continue;
if (left*right <= 0) ret++;
left = right;
}
return ret + 1;
}
};
题二.最长递增子序列(LeetCode)
题目描述
给你一个整数数组
nums
,找到其中最长严格递增子序列的长度。子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,
[3,6,2,7]
是数组[0,3,1,6,2,2,7]
的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
题目分析
因为我们最后需要的是子序列的长度,因此我们可以采用贪心策略:我们不关心这个最长子序列长什么样,我们仅关心“最后一个元素”是谁,跟nums[i]是否能拼在一起。且我们注意到拼的一起的元素在不影响序列长度的条件下,越小越好。
我们可以采用逐步交换的思路,初始化子序列第一个元素,然后比较下一个元素。
- 若是下一个元素X小于前一个位置的元素,则可以考虑替换。
- 若是下一个元素X大于前一个位置的元素,则可以将其与下一个位置的元素比较。
持续如上操作,直至序列结束。
优化时间复杂度:可以用二分查找的方法确定X的插入位置。
题解
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
vector<int> ret;
ret.push_back(nums[0]);
for (int i = 1; i < n; i++)
{
if (nums[i] > ret.back())
{
ret.push_back(nums[i]);
}
else
{
int left = 0, right = ret.size() - 1;
while (left < right)
{
int mid = (left + right) >> 1;
if (nums[i] > ret[mid]) left = mid + 1;
else right = mid;
}
ret[left] = nums[i];
}
}
return ret.size();
}
};
题三.递增三元子序列(LeetCode)
题目描述
给你一个整数数组
nums
,判断这个数组中是否存在长度为3
的递增子序列。如果存在这样的三元组下标
(i,j,k)
且满足i<j<k
,使得nums[i]<nums[j]<nums[k]
,返回true
;否则,返回false
。
示例 1:
输入:nums = [1,2,3,4,5]
输出:true
解释:任何 i < j < k 的三元组都满足题意示例 2:
输入:nums = [5,4,3,2,1]
输出:false
解释:不存在满足题意的三元组示例 3:
输入:nums = [2,1,5,0,4,6]
输出:true
解释:三元组 (3, 4, 5) 满足题意,因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6
题目分析
简化版的题二,但是可以简化一下空间,优化一下思路:只需要满足三元组,就可以return true。
题解
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
int n = nums.size();
vector<int> ret;
ret.push_back(nums[0]);
ret.push_back(INT_MAX);
for (int i = 1; i < n; i++)
{
if (nums[i] > ret[1]) return true;
else if (nums[i] > ret[0]) ret[1] = nums[i];
else ret[0] = nums[i];
}
return false;
}
};