Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 56f25cf

Browse files
✨feat: Add 432
1 parent 60e1a33 commit 56f25cf

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed

‎Index/哈希表.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
| [268. 丢失的数字](https://leetcode-cn.com/problems/missing-number/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/missing-number/solution/gong-shui-san-xie-yi-ti-wu-jie-pai-xu-ji-te3s/) | 简单 | 🤩🤩🤩🤩 |
1818
| [299. 猜数字游戏](https://leetcode-cn.com/problems/bulls-and-cows/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/bulls-and-cows/solution/gong-shui-san-xie-jian-dan-mo-ni-ti-by-a-tdhs/) | 中等 | 🤩🤩🤩🤩 |
1919
| [318. 最大单词长度乘积](https://leetcode-cn.com/problems/maximum-product-of-word-lengths/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/maximum-product-of-word-lengths/solution/gong-shui-san-xie-jian-dan-wei-yun-suan-cqtxq/) | 中等 | 🤩🤩🤩🤩 |
20+
| [432. 全 O(1) 的数据结构](https://leetcode-cn.com/problems/all-oone-data-structure/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/all-oone-data-structure/solution/by-ac_oier-t26d/) | 困难 | 🤩🤩🤩 |
2021
| [447. 回旋镖的数量](https://leetcode-cn.com/problems/number-of-boomerangs/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-boomerangs/solution/gong-shui-san-xie-shu-ju-jie-gou-yun-yon-evu2/) | 中等 | 🤩🤩🤩🤩 |
2122
| [451. 根据字符出现频率排序](https://leetcode-cn.com/problems/sort-characters-by-frequency/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/sort-characters-by-frequency/solution/gong-shui-san-xie-shu-ju-jie-gou-yun-yon-gst9/) | 中等 | 🤩🤩🤩🤩 |
2223
| [460. LFU 缓存](https://leetcode-cn.com/problems/lfu-cache/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/lfu-cache/solution/gong-shui-san-xie-yun-yong-tong-pai-xu-s-53m3/) | 困难 | 🤩🤩🤩🤩🤩 |

‎Index/链表.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
| [237. 删除链表中的节点](https://leetcode-cn.com/problems/delete-node-in-a-linked-list/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/delete-node-in-a-linked-list/solution/gong-shui-san-xie-jian-dan-lian-biao-mo-rovcb/) | 简单 | 🤩🤩🤩 |
1818
| [382. 链表随机节点](https://leetcode-cn.com/problems/linked-list-random-node/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/linked-list-random-node/solution/gong-shui-san-xie-xu-shui-chi-chou-yang-1lp9d/) | 中等 | 🤩🤩🤩🤩🤩 |
1919
| [430. 扁平化多级双向链表](https://leetcode-cn.com/problems/flatten-a-multilevel-doubly-linked-list/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/flatten-a-multilevel-doubly-linked-list/solution/gong-shui-san-xie-yi-ti-shuang-jie-di-gu-9wfz/) | 中等 | 🤩🤩🤩🤩🤩 |
20+
| [432. 全 O(1) 的数据结构](https://leetcode-cn.com/problems/all-oone-data-structure/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/all-oone-data-structure/solution/by-ac_oier-t26d/) | 困难 | 🤩🤩🤩🤩 |
2021
| [460. LFU 缓存](https://leetcode-cn.com/problems/lfu-cache/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/lfu-cache/solution/gong-shui-san-xie-yun-yong-tong-pai-xu-s-53m3/) | 困难 | 🤩🤩🤩🤩🤩 |
2122
| [725. 分隔链表](https://leetcode-cn.com/problems/split-linked-list-in-parts/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/split-linked-list-in-parts/solution/gong-shui-san-xie-jing-dian-lian-biao-ju-9yj4/) | 简单 | 🤩🤩🤩🤩🤩 |
2223
| [1600. 皇位继承顺序](https://leetcode-cn.com/problems/throne-inheritance/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/throne-inheritance/solution/gong-shui-san-xie-shi-yong-dan-xiang-lia-7t65/) | 中等 | 🤩🤩🤩 |
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[432. 全 O(1) 的数据结构](https://leetcode-cn.com/problems/all-oone-data-structure/solution/by-ac_oier-t26d/)** ,难度为 **困难**
4+
5+
Tag : 「双向链表」、「哈希表」
6+
7+
8+
9+
请你设计一个用于存储字符串计数的数据结构,并能够返回计数最小和最大的字符串。
10+
11+
实现 `AllOne` 类:
12+
13+
* `AllOne()` 初始化数据结构的对象。
14+
* `inc(String key)` 字符串 `key` 的计数增加 1ドル$ 。如果数据结构中尚不存在 `key` ,那么插入计数为 1ドル$ 的 `key`
15+
* `dec(String key)` 字符串 `key` 的计数减少 1ドル$ 。如果 `key` 的计数在减少后为 0ドル$ ,那么需要将这个 `key` 从数据结构中删除。测试用例保证:在减少计数前,`key` 存在于数据结构中。
16+
* `getMaxKey()` 返回任意一个计数最大的字符串。如果没有元素存在,返回一个空字符串 `""`
17+
* `getMinKey()` 返回任意一个计数最小的字符串。如果没有元素存在,返回一个空字符串 `""`
18+
19+
20+
示例:
21+
```
22+
输入
23+
["AllOne", "inc", "inc", "getMaxKey", "getMinKey", "inc", "getMaxKey", "getMinKey"]
24+
[[], ["hello"], ["hello"], [], [], ["leet"], [], []]
25+
26+
输出
27+
[null, null, null, "hello", "hello", null, "hello", "leet"]
28+
29+
解释
30+
AllOne allOne = new AllOne();
31+
allOne.inc("hello");
32+
allOne.inc("hello");
33+
allOne.getMaxKey(); // 返回 "hello"
34+
allOne.getMinKey(); // 返回 "hello"
35+
allOne.inc("leet");
36+
allOne.getMaxKey(); // 返回 "hello"
37+
allOne.getMinKey(); // 返回 "leet"
38+
```
39+
40+
提示:
41+
* 1ドル <= key.length <= 10$
42+
* `key` 由小写英文字母组成
43+
* 测试用例保证:在每次调用 `dec` 时,数据结构中总存在 `key`
44+
* 最多调用 `inc``dec``getMaxKey``getMinKey` 方法 5ドル * 10^4$ 次
45+
46+
---
47+
48+
### 双向链表 + 哈希表
49+
50+
题目要求我们支持 $O(1)$ 的查询和修改,其中查询只需返回任意一个计数次数「最多」和「最少」的元素即可(如果有)。
51+
52+
虽然插入的字符串长度不超过 10ドル$(该数据范围的含义为字符串的哈希计算消耗可看作常数),但单纯的使用「哈希表」仅能做到 $O(1)$ 的计数,无法做到 $O(1)$ 查询。
53+
54+
**我们可以采取和 [实现一个 LRUCache](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247486820&idx=1&sn=2055864e10848bce55afc4e2feda79a7&chksm=fd9ca67bcaeb2f6d2038706e32cafffd701d3d6b71b60c52a568ed6dc0e268d93f8a78498105&token=146288031&lang=zh_CN#rd) & [实现一个 LFUCache](https://mp.weixin.qq.com/s?__biz=MzU4NDE3MTEyMA==&mid=2247486856&idx=1&sn=59b2ed57e4a75eac0e63fc0cf08bed5d&chksm=fd9ca697caeb2f81f83cdc741375f9130c3fa40463edb4ae98d702354ca106378fdfe23735ad&token=146288031&lang=zh_CN#rd) 类似的思路,通过自定义节点并手写双链表来实现。**
55+
56+
定义一个节点类 `Node`,除了包含用于实现双向链表的 `left``right` 以外,还包含一个数值类型的变量 `val`, 用于记录该节点存储的是计数次数为多少的元素,以及一个 `Set` 类型的容器,用于支持 $O(1)$ 插入和删除元素,记作 `set`
57+
58+
同时为了快速知道某个字符串属于哪个 `Node`,我们还需要开一个「哈希表」进行定位(以字符串为哈希表的键,字符串所在 `Node` 作为值),当定位到字符串对应的 `Node` 之后则可以利用「双向链表」的 $O(1)$ 增加/修改/删除。
59+
60+
在双向链表中,起始只有两个哨兵节点 `hh``tt` ,当进行若干 `inc/dec` 操作后的基本形态为:
61+
62+
![](https://pic.leetcode-cn.com/1647386842-kYgoaN-image.png)
63+
64+
对应几个操作:
65+
66+
`inc/dec` 操作:当对一个字符串 `key` 进行「增加计数」或「减少计数」时,先在哈希表中看 `key` 是否存在:
67+
68+
* 若存在:根据其所属的 `Node` 的计数 `cnt` 为多少,并结合当前是「增加计数」还是「减少计数」来决定是找 `Node` 的「右节点」还是「左节点」,同时检查相邻节点的计数值 `cnt` 是否为目标值,对应要检查数值是 $cnt + 1$ 和 $cnt - 1$:
69+
* 若相邻节点的 `cnt` 为目标值:即目标节点存在,将 `key` 从原 `Node``set` 集合中移除,并添加到目标节点的集合中,更新哈希表;
70+
* 若相邻节点的 `cnt` 不是目标值:则需要创建相应的目标节点,并构建双向链表关系,把 `key` 存入新创建的目标节点,更新哈希表。
71+
* 若不存在(只能是 `inc` 操作):查找是否存在 $cnt = 1$ 的节点(也就是检查 `hh.right` 节点的计数值):
72+
* 如果存在 $cnt = 1$ 的目标节点:将 `key` 添加到目标节点的 `set` 集合中,更新哈希表;
73+
* 若不存在 $cnt = 1$ 的目标节点:创建相应的节点,并构建双向关系,并构建双向链表关系,把 `key` 存入新创建的目标节点,更新哈希表。
74+
75+
`getMaxKey/getMinKey` 操作:分别从 `tt.left``hh.right` 中尝试查找,如果存在非哨兵节点,则从节点的 `set` 集合中取任意元素进行返回,否则返回空串。
76+
77+
最后,为了确保 `getMaxKey/getMinKey` 操作能够严格 $O(1),ドル我们在进行 `inc/dec` 操作时我们需要对一些 `set` 容量为 0ドル$ 的节点进行释放,即解除其所在双向链表的关系。
78+
79+
代码:
80+
```Java
81+
class AllOne {
82+
83+
class Node {
84+
int cnt;
85+
Set<String> set = new HashSet<>();
86+
Node left, right;
87+
Node(int _cnt) {
88+
cnt = _cnt;
89+
}
90+
}
91+
92+
Node hh, tt;
93+
Map<String, Node> map = new HashMap<>();
94+
public AllOne() {
95+
hh = new Node(-1000); tt = new Node(-1000);
96+
hh.right = tt; tt.left = hh;
97+
}
98+
99+
void clear(Node node) {
100+
if (node.set.size() == 0) {
101+
node.left.right = node.right;
102+
node.right.left = node.left;
103+
}
104+
}
105+
106+
public void inc(String key) {
107+
if (map.containsKey(key)) {
108+
Node node = map.get(key);
109+
node.set.remove(key);
110+
int cnt = node.cnt;
111+
Node next = null;
112+
if (node.right.cnt == cnt + 1) {
113+
next = node.right;
114+
} else {
115+
next = new Node(cnt + 1);
116+
next.right = node.right;
117+
next.left = node;
118+
node.right.left = next;
119+
node.right = next;
120+
}
121+
next.set.add(key);
122+
map.put(key, next);
123+
clear(node);
124+
} else {
125+
Node node = null;
126+
if (hh.right.cnt == 1) {
127+
node = hh.right;
128+
} else {
129+
node = new Node(1);
130+
node.right = hh.right;
131+
node.left = hh;
132+
hh.right.left = node;
133+
hh.right = node;
134+
}
135+
node.set.add(key);
136+
map.put(key, node);
137+
}
138+
}
139+
140+
public void dec(String key) {
141+
Node node = map.get(key);
142+
node.set.remove(key);
143+
int cnt = node.cnt;
144+
if (cnt == 1) {
145+
map.remove(key);
146+
} else {
147+
Node prev = null;
148+
if (node.left.cnt == cnt - 1) {
149+
prev = node.left;
150+
} else {
151+
prev = new Node(cnt - 1);
152+
prev.right = node;
153+
prev.left = node.left;
154+
node.left.right = prev;
155+
node.left = prev;
156+
}
157+
prev.set.add(key);
158+
map.put(key, prev);
159+
}
160+
clear(node);
161+
}
162+
163+
public String getMaxKey() {
164+
Node node = tt.left;
165+
for (String str : node.set) return str;
166+
return "";
167+
}
168+
169+
public String getMinKey() {
170+
Node node = hh.right;
171+
for (String str : node.set) return str;
172+
return "";
173+
}
174+
}
175+
```
176+
* 时间复杂度:$O(1)$
177+
* 空间复杂度:$O(n)$
178+
179+
---
180+
181+
### 最后
182+
183+
这是我们「刷穿 LeetCode」系列文章的第 `No.432` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
184+
185+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
186+
187+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
188+
189+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
190+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /