846. Hand of Straights。

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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值