leetcode 659.分割数组为连续子序列

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;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值