LC 846. 一手顺子

题目描述

这是 LeetCode 上的 846. 一手顺子 ,难度为 中等

Alice 手中有一把牌,她想要重新排列这些牌,分成若干组,使每一组的牌数都是 groupSize ,并且由 groupSize 张连续的牌组成。

给你一个整数数组 hand 其中 hand[i] 是写在第 i 张牌,和一个整数 groupSize

如果她可能重新排列这些牌,返回 true ;否则,返回 false

示例 1:

1
2
3
4
5
输入:hand = [1,2,3,6,2,3,4,7,8], groupSize = 3

输出:true

解释:Alice 手中的牌可以被重新排列为 [1,2,3][2,3,4][6,7,8]

示例 2:
1
2
3
4
5
输入:hand = [1,2,3,4,5], groupSize = 4

输出:false

解释:Alice 手中的牌无法被重新排列成几个大小为 4 的组。

提示:

  • $1 <= hand.length <= 10^4$
  • $0 <= hand[i] <= 10^9$
  • $1 <= groupSize <= hand.length$

模拟 + 哈希表 + 优先队列(堆)

为了方便,我们令 $m = groupSize$。

题目要求我们将 $hand$ 分为若干份大小为 $m$ 的顺子。

在给定 $hand$ 的情况下,划分方式唯一确定,因此本质上这是一个「模拟」的过程。

具体的,我们可以使用「哈希表」对 $hand$ 中的数值进行「出现次数」统计,并用于后续 实时 维护剩余元素的出现次数。

然后使用优先队列维护(小根堆)所有的 $hand[i]$。每次从优先队列(堆)中取出堆顶元素 $t$ 来 尝试作为「顺子」的发起点/首个元素(当然 $t$ 能够作为发起点的前提是 $t$ 仍是剩余元素,即实时维护的出现次数不为 $0$ ),然后用 $t$ 作为发起点/首个元素构造顺子,即对 $[t, t + 1, … , t + m - 1]$ 元素的出现次数进行 $-1$ 操作。

若构造过程中没有出现「剩余元素出现次数」不足以 $-1$ 的话,说明整个构造过程没有冲突,返回 True,否则返回 False

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public boolean isNStraightHand(int[] hand, int m) {
Map<Integer, Integer> map = new HashMap<>();
PriorityQueue<Integer> q = new PriorityQueue<>((a,b)->a-b);
for (int i : hand) {
map.put(i, map.getOrDefault(i, 0) + 1);
q.add(i);
}
while (!q.isEmpty()) {
int t = q.poll();
if (map.get(t) == 0) continue;
for (int i = 0; i < m; i++) {
int cnt = map.getOrDefault(t + i, 0);
if (cnt == 0) return false;
map.put(t + i, cnt - 1);
}
}
return true;
}
}

  • 时间复杂度:令 $n$ 为数组 hand 长度,使用哈希表进行次数统计的复杂度为 $O(n)$;将所有元素从堆中存入和取出的复杂度为 $O(n\log{n})$。整体复杂度为 $O(n\log{n})$
  • 空间复杂度:$O(n)$

最后

这是我们「刷穿 LeetCode」系列文章的第 No.846 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。