Alice has a hand of cards, given as an array of integers.
Now she wants to rearrange the cards into groups so that each group is size W, and consists of W consecutive cards.
Return true if and only if she can.
Example 1:
Input: hand = [1,2,3,6,2,3,4,7,8], W = 3
Output: true
Explanation: Alice’s hand can be rearranged as [1,2,3],[2,3,4],[6,7,8].
Example 2:
Input: hand = [1,2,3,4,5], W = 4
Output: false
Explanation: Alice’s hand can’t be rearranged into groups of 4.
Note:
- 1 <= hand.length <= 10000
- 0 <= hand[i] <= 10^9
- 1 <= W <= hand.length
原文链接:https://leetcode.com/problems/hand-of-straights/
给定一个数组和一个W,需要我们将该数组分成若干组,需要满足每一组有W个数字元素,并且W个数字必须连续。
刚开始我的思路比较简单,首先把数组中所有的元素放到一个Map中,数组的元素作为Map中的key,该元素出现的次数作为Map的value。以官方例子中的数组:[1,2,3,6,2,3,4,7,8] 。
放到Map中后,首先用数组的总个数除以W,得到的n就是最后一共能够分为多少组,当然如果不能整除的话就直接返回false即可。按照题意,每一个分组的数字都是连续的,我们可以先找到Map中第一个value不为0的元素(value代表该元素可用于进行分组的个数),然后如果能够满足题意的话,那么该元素接下来的W个元素在Map中存储的个数都是大于0的,并且遍历一次元素就代表对该元素进行了分组,所以需要将其value减去一。如果不满足题意的话,就会遇到某个元素的value为0,这时直接返回false即可。
将例子中进行第一次遍历分组,可以将 {1,2,3} 分出来:
进行第二次分组,将 {2,3,4}分出来:
第三次也就是最后一次,将 {6,7,8} 分出来:
如果其中不满足的话,就会出现value等于0的情况。
class Solution {
public:
bool isNStraightHand(vector<int>& hand, int W) {
if(hand.size()==0 || hand.size()%W!=0)
return false;
map<int,int> PAIR;
for(int num : hand) {//将数组中的元素放到map中
PAIR[num]++;
}
map<int,int>::iterator it = PAIR.begin();
//while(it != PAIR.end()){
//cout << "first:" << it->first << ",second:" << it->second << endl;
//it++;
//}
int i=0,n=hand.size()/W;
int start,end;
while(i < n) {
while(it != PAIR.end()) {// 找到最小的没有分配过的元素
if(it->second > 0) {
start = it->first;
break;
}
it++;
}
//cout << "start:" << start << endl;
end = start + W;
while(start < end) { // 接下来的W个应该在map中都有存放
//cout << start << " " << "," << PAIR[start] << endl;
if(PAIR[start]>0){ // 如果大于0则说明该元素还有剩余,说明可以用来组成连续的数组
PAIR[start]--; // 使用一次之后,将该元素个数减1
start++;// 因为要求是连续的,所以start++就是代表下一个应该出现的数字
} else {
return false;
}
}
//cout << endl;
i++;
}
return true;
}
};
看到提交的里面有一种类似的思路,比较简洁但是思路差不多:
class Solution {
public:
bool isNStraightHand(vector<int>& hand, int W) {
map<int,int> m;
for(auto h : hand)
{
m[h]++;
}
for(auto i : m)
{
if(i.second == 0)
{
continue;
}
for(int j = i.first+W-1 ; j >= i.first ; j--)
{
m[j]-=i.second;
if(m[j] < 0)
{
return false;
}
}
}
return true;
}
};
另一种比较精妙的思路,既然是分为多个分组,每个分组有W个元素。可以用数组中的元素对W取余数,可以将数组中的所有元素都归到0到W-1中,比如例子中的 :[1,2,3,6,2,3,4,7,8] , 分别对W=3取余数,那么全部可以归成 {0,1,2},这样我们只需要统计 {0,1,2}的出现次数是否相等即可,如果相等则说明可以划分为若干个W长度的分组,否则不可以。
class Solution {
public:
bool isNStraightHand(vector<int>& hand, int W) {
int n = hand.size();
if (n < 1 || W < 1 || n < W)
{
return false;
}
if (n % W != 0)
{
return false;
}
vector<int> count (W, 0);
for (const int& h : hand)
{
++count[h % W]; // 将所有的连续数列压缩到 0 到 W-1 之间,这样0到W-1之间的数字出现的次数都一样
}
int expect = count.front();// 找出0出现的次数,后面的次数都应该等于这个
for(const int& c : count)
{
if (c != expect)
{
return false;
}
}
return true;
}
};