659. Split Array into Consecutive Subsequences 堆、优先级队列、哈希表

You are given an integer array sorted in ascending order (may contain duplicates), you need to split them into several subsequences, where each subsequences consist of at least 3 consecutive integers. Return whether you can make such a split.

Example 1:

Input: [1,2,3,3,4,5]
Output: True
Explanation:
You can split them into two consecutive subsequences : 
1, 2, 3
3, 4, 5

Example 2:

Input: [1,2,3,3,4,4,5,5]
Output: True
Explanation:
You can split them into two consecutive subsequences : 
1, 2, 3, 4, 5
3, 4, 5

Example 3:

Input: [1,2,3,4,4,5]
Output: False

Note:

  1. The length of the input is in range of [1, 10000]
思路 1:使用两个哈希表,这里是贪心的思想:如果当前的数可以放在之前的序列之后,就放,若果不行,就形成一个新的序列。下面的代码的思路是:通过设置一个chance数组,提供这样的后续延续机会,因为数组是排过序的,遇到重复的数首先是构成新的数列。
class Solution {
public:
    bool isPossible(vector<int>& nums) {
        //这个题就是模拟取数的过程
        //在取数的时候,刚开始的时候取三个数,遇到新的数加在之前形成的数之后;遇到重复的数,重新行程一个三数组合。
        //下面使用两个哈希表,一个统计所有数出现的次数,另外一个是取数的时候做统计。
        //关键是另外一个哈希表的意义:表示当前数的潜在填补可能数,比如chance[i]==2,说明i这个数在之前有两个已经形成的序列后可以放进去。
        //每当一个数用上了,就在freq中减掉,如果潜在位置数也用了一个,也减1
        //在下面的代码中进行详细的说明
        
        //问题的核心是:对于已经形成的序列,后面都有一个位置可以再添加一个数,我们成他为一个机会。就是说如果这个机会对应的数如果不能用于组成另外一个序列,他还可以有这个机会添加到已经构成的序列后面。因为是排过序的,如果没有重复的数,当前的序列会不断扩充。
        //遇到了重复的数,当前的已知序列没有能提供这个机会添加到后面,那么这个数要行程一个长度为3的新序列,同时也增加一个尾添加机会
        unordered_map<int,int> freq;//统计所有数的出现次数
        unordered_map<int,int> chance;
        for(int i=0;i<nums.size();i++)
            freq[nums[i]]++;
        for(auto i:nums)
        {
            if(freq[i]==0) continue;//当前数已经用完
            if(chance[i]>0)//有序列可以有位置包养当前的数,就使用一个位置
            {
                chance[i]--;//这个位置少了一个机会
                chance[i+1]++;//下一个位置多了一个容纳下一个数的位置
            }
            else if(freq[i+1]>0&&freq[i+2]>0)//现在没有序列可以容纳当前的元素,说明出现了重复的值,或者是中间断裂。因此需要形成一个新的序列
            {
                freq[i+1]--;
                freq[i+2]--;
                chance[i+3]++;
            }
            else return false;
            freq[i]--;
        }
        return true;
        
    }
};
思路2:
这种思路类似。通过设置一个哈希表,键是当前序列的最后一个数,值是一个最小堆,意思是构成一个以这个数结尾的对应的所有序列,序列长度最小的那个排在堆顶。对于一个新的数,首先寻找他能不能在放到别的序列后面,而且是找长度小的那个放,放了之后,将这个堆顶弹出,在当前数结尾的堆中加入新的长度值。如果找不到现有序列去放这个数,就要开辟一个新的序列结尾。同时设置一个 变量。每当形成一个新的序列,need_more加1,当这个序列达到3时,need_more减一。
因为长度大于3序列已经满足要求了,长度小于3的序列需要首先满足。
如果最终need_more 不等于0,说明有的序列没有达到三个
class Solution {
public:
	bool isPossible(vector<int>& nums)
	{
		unordered_map<int, priority_queue<int, vector<int>, std::greater<int>>> backs;

		// Keep track of the number of sequences with size < 3
		int need_more = 0;

		for (int num : nums)
		{
			if (! backs[num - 1].empty())
			{	// There exists a sequence that ends in num-1
				// Append 'num' to this sequence
				// Remove the existing sequence
				// Add a new sequence ending in 'num' with size incremented by 1 
				int count = backs[num - 1].top();
				backs[num - 1].pop();
				backs[num].push(++count);

				if (count == 3)
					need_more--;
			}
			else
			{	// There is no sequence that ends in num-1
				// Create a new sequence with size 1 that ends with 'num'
				backs[num].push(1);
				need_more++;
			}
		}
		return need_more == 0;
	}
};



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值