Given an integer array hand and an integer groupSize, determine whether you can rearrange all cards into groups of size groupSize where each group consists of consecutively increasing values (each step = 1). Return true if possible, false otherwise.
1 ≤ hand.length ≤ 10000 ≤ hand[i] ≤ 10001 ≤ groupSize ≤ hand.lengthThe key greedy insight: always form the group starting from the smallest available card. Why? Because the smallest card can only start a group — it can never be the 2nd, 3rd, or later card in any group (those require something smaller). If we delay using the smallest card, we'll eventually run out of cards to pair it with.
So the algorithm is:
len(hand) % groupSize ≠ 0 → immediately false.num that still has cards available, try to form a group [num, num+1, ..., num+groupSize-1].false.Sort the hand so we always encounter the smallest available card first. Use a Counter to track remaining counts. For each card in sorted order, if its count is still positive, try to consume a full group starting there.
def isNStraightHand(hand, groupSize):
if len(hand) % groupSize:
return False
count = Counter(hand)
hand.sort()
for num in hand:
if count[num]: # still has cards at this value
for i in range(num, num + groupSize):
if not count[i]:
return False
count[i] -= 1
return TrueInstead of sorting the original array, maintain a min-heap of distinct card values. At each step, the heap top gives the smallest remaining value. Form a group from it, decrementing counts. When a count hits zero, it must be the heap's current minimum — pop it. If it isn't the minimum, some smaller value is stranded and we return false.
def isNStraightHand(hand, groupSize):
if len(hand) % groupSize:
return False
count = {}
for n in hand:
count[n] = 1 + count.get(n, 0)
minH = list(count.keys())
heapq.heapify(minH)
while minH:
first = minH[0]
for i in range(first, first + groupSize):
if i not in count:
return False
count[i] -= 1
if count[i] == 0:
if i != minH[0]: # can't remove mid-heap safely
return False
heapq.heappop(minH)
return TrueTrack how many groups are currently open (started but not yet complete). Process values in sorted order. At each value num:
num but num isn't consecutive to the previous value → impossible.count[num] < open_groups → not enough cards to extend all open groups → impossible.count[num] - open_groups) starts new groups.groupSize, those groups close — subtract their count from open_groups.def isNStraightHand(hand, groupSize):
if len(hand) % groupSize != 0:
return False
count = Counter(hand)
q = deque()
last_num, open_groups = -1, 0
for num in sorted(count):
if (open_groups > 0 and num > last_num + 1) or open_groups > count[num]:
return False
q.append(count[num] - open_groups)
last_num = num
open_groups = count[num]
if len(q) == groupSize:
open_groups -= q.popleft()
return open_groups == 0| Approach | Time | Space | Notes |
|---|---|---|---|
| Sorting + Counter | O(n log n) | O(n) | Simplest to implement |
| Min-Heap | O(n log n) | O(n) | No need to sort original array |
| Ordered Map + Queue | O(n log n) | O(n) | Most space-efficient in practice; elegant |
This problem is the template for a class of problems where you must partition elements into valid groups. The pattern works when:
k instead of groupSize).if i != minH[0]: return False worksWhen count[i] drops to zero, card value i is exhausted. If i is not currently the heap minimum, there exists some smaller value j < i that still has cards — but no group can extend to include j anymore (all groups passing through j would need to have also included j-1, j-2, etc.). That smaller value is now stranded. Returning false immediately is correct.
If len(hand) % groupSize ≠ 0, you can't form complete groups — return false immediately. Without this, the loop may silently "succeed" on an invalid input.
If you start a group from an arbitrary card, you might leave smaller cards stranded. The sort (or heap) enforces that the smallest unplaced card is always processed first.
In Python, Counter doesn't iterate in sorted order. Iterating over hand (after sorting) or sorted(count) is required. Using count.keys() directly gives arbitrary order in older Python versions.
hand=[1], groupSize=1 → true (single card is its own group)hand=[1,2,3], groupSize=2 → false (3 cards, groupSize=2: 3%2≠0)hand=[1,1,2,2,3,3], groupSize=3 → true: [1,2,3] and [1,2,3]hand=[1,2,3,4,5,6], groupSize=3 → true: [1,2,3] and [4,5,6] — but also could be [1,2,3],[2,3,4],[...]. Greedy picks [1,2,3],[4,5,6]. ✓
Discussion
…