leetcode 659.分割数组为连续子序列
题干
给你一个按升序排序的整数数组 num(可能包含重复数字),请你将它们分割成一个或多个子序列,其中每个子序列都由连续整数组成且长度至少为 3 。
如果可以完成上述分割,则返回 true ;否则,返回 false 。
示例 1:
输入: [1,2,3,3,4,5]
输出: True
解释:
你可以分割出这样两个连续子序列 :
1, 2, 3
3, 4, 5
示例 2:
输入: [1,2,3,3,4,4,5,5]
输出: True
解释:
你可以分割出这样两个连续子序列 :
1, 2, 3, 4, 5
3, 4, 5
示例 3:
输入: [1,2,3,4,4,5]
输出: False
提示:
输入的数组长度范围为 [1, 10000]
题解
并不能过的第一版,虽然理解上很直观,但是在更新高度起点的时候没有处理好。
看看就图一乐。
class Solution {
public:
bool isPossible(vector<int>& nums) {
int n = nums.size();
if(n <= 2){
return false;
}
vector<int> pileHeight(n);
int prePileValue = 1,currentPileValue = 0;
int samePileCount = 1;
int pileCount = 0;
pileHeight[1] = 0;
for(int i = 1 ; i < n ; ++i){
if(nums[i] != nums[i-1]){
pileHeight[1] = i;
}
//出现了前一个数不同的数
if(nums[i] != nums[i-1]){
//结算上一个堆的大小,堆数+1
pileCount++;
//高度上升,记录起点
if(currentPileValue > prePileValue){
for(int j = prePileValue ; j <= currentPileValue ; ++j){
pileHeight[currentPileValue] = pileCount;
}
}else if(currentPileValue < prePileValue){
//高度下降,获得等高宽度
//处理峰顶的情况
if(samePileCount < 3){
return false;
}
if(pileCount - pileHeight[currentPileValue] + 1 < 3){
return false;
}
currentPileValue = 1;
}else{
//高度相等
samePileCount++;
currentPileValue = 1;
}
}else{
currentPileValue++;
}
}
//处理弹出循环的情况,即最后一个堆没有结算的情况。此时又有
if(currentPileValue > prePileValue){
return false;
}else{
if(n - pileHeight[currentPileValue] < 3){
return false;
}else{
return true;
}
}
}
};
/*
猜测算法:
一个连续子序列在重复数字第一次减少时终止,比如:
1 2 3 3 4 4 5 6
即分为1 2 3 4 和 4 5 6
1 2 3 3 3 4 4 4 5 5 6 6 7
分为 1 2 3 4、3 4 5 6、3 4 5 6 7
(第一个1234分出来后34的重复次数减小为2了,所以跟67相同)
1 2 3 3 4 5
1 1 2 2 3 3 4
归纳出更为简单的规律:记某个数字重复出现的次数为X,则对于每个数字,有数列Xn。对于Xn,对于任意连续的若干项,若相同值的项数存在<3的情况则无法分割。
记某个数字重复出现的次数为X,则对于每个数字有数列Xn,将数列图像化为直方图,高度代表每个数出现的次数。从图像上理解,分割出的连续子序列,相当于
在直方图上某一高度截取的几个值的宽度大于等于3的一段,而且这几个值同高度的部分一定是连续的。
所以开辟数组,当高度上升时,存放某个高度的起点,高度下降时,比较相同高度的起点,若宽度<3则不可分割
*/
参考官方题解的解法,利用哈希表直接通过键访问的特性,用哈希表储存数出现的个数
对于每个数,如果存在以这个数前一位的数为结尾的子序列,则将这个数加到其尾部。
若不存在,则检查这个数后两个数,如果存在未被使用的后两个数,则新建一个长度为三的子序列。
由于哈希表的特性不用担心边界情况。
class Solution {
public:
bool isPossible(vector<int>& nums) {
int n = nums.size();
unordered_map<int,int> num;
unordered_map<int,int> end;
if(n <= 2){
return false;
}
for(auto i : nums){
num[i]++;
}
for(auto i : nums){
//i没被用完
if(num[i] > 0){
//存在以i-1为结尾的子序列
if(end[i-1] > 0){
end[i-1]--;
end[i]++;
num[i]--;
}else{
//不存在以i-1为结尾的子序列,只能新开辟一个子序列。查询i+1 i+2是否被用尽
if(num[i+1] > 0 && num[i+2] > 0){
num[i]--;
num[i+1]--;
num[i+2]--;
end[i+2]++;
}else{
return false;
}
}
}
}
return true;
}
};