全 O(1) 的数据结构
难度:
标签:
题目描述
请你设计一个用于存储字符串计数的数据结构,并能够返回计数最小和最大的字符串。
实现 AllOne
类:
AllOne()
初始化数据结构的对象。inc(String key)
字符串key
的计数增加1
。如果数据结构中尚不存在key
,那么插入计数为1
的key
。dec(String key)
字符串key
的计数减少1
。如果key
的计数在减少后为0
,那么需要将这个key
从数据结构中删除。测试用例保证:在减少计数前,key
存在于数据结构中。getMaxKey()
返回任意一个计数最大的字符串。如果没有元素存在,返回一个空字符串""
。getMinKey()
返回任意一个计数最小的字符串。如果没有元素存在,返回一个空字符串""
。
注意:每个函数都应当满足 O(1)
平均时间复杂度。
示例:
输入 ["AllOne", "inc", "inc", "getMaxKey", "getMinKey", "inc", "getMaxKey", "getMinKey"] [[], ["hello"], ["hello"], [], [], ["leet"], [], []] 输出 [null, null, null, "hello", "hello", null, "hello", "leet"] 解释 AllOne allOne = new AllOne(); allOne.inc("hello"); allOne.inc("hello"); allOne.getMaxKey(); // 返回 "hello" allOne.getMinKey(); // 返回 "hello" allOne.inc("leet"); allOne.getMaxKey(); // 返回 "hello" allOne.getMinKey(); // 返回 "leet"
提示:
1 <= key.length <= 10
key
由小写英文字母组成- 测试用例保证:在每次调用
dec
时,数据结构中总存在key
- 最多调用
inc
、dec
、getMaxKey
和getMinKey
方法5 * 104
次
代码结果
运行时间: 96 ms, 内存: 0.0 MB
/*
* 思路:
* 使用Java Stream实现题目要求的方法比较复杂,
* 因为Stream的操作通常是无状态的,而本题要求的操作需要状态保持。
* 但是可以通过Java 8+的集合框架来实现,
* 主要利用Map和List来实现对字符串的计数和排序。
*/
import java.util.*;
import java.util.stream.Collectors;
class AllOne {
private Map<String, Integer> keyCount;
private Map<Integer, Set<String>> countKey;
public AllOne() {
keyCount = new HashMap<>();
countKey = new HashMap<>();
}
public void inc(String key) {
int count = keyCount.getOrDefault(key, 0);
keyCount.put(key, count + 1);
if (count > 0) {
countKey.get(count).remove(key);
if (countKey.get(count).isEmpty()) countKey.remove(count);
}
countKey.computeIfAbsent(count + 1, k -> new HashSet<>()).add(key);
}
public void dec(String key) {
int count = keyCount.get(key);
if (count == 1) {
keyCount.remove(key);
} else {
keyCount.put(key, count - 1);
}
countKey.get(count).remove(key);
if (countKey.get(count).isEmpty()) countKey.remove(count);
if (count > 1) countKey.computeIfAbsent(count - 1, k -> new HashSet<>()).add(key);
}
public String getMaxKey() {
return countKey.isEmpty() ? "" : countKey.entrySet().stream().max(Map.Entry.comparingByKey()).get().getValue().iterator().next();
}
public String getMinKey() {
return countKey.isEmpty() ? "" : countKey.entrySet().stream().min(Map.Entry.comparingByKey()).get().getValue().iterator().next();
}
}
解释
方法:
这个题解使用双向链表和哈希表来实现AllOne类的功能。链表中的每个节点包含一个计数值和一个存储对应计数值的键的集合。哈希表用于快速查找每个键所在的链表节点。在inc和dec操作中,通过更新链表节点的键集合和在链表中插入或删除节点来维护计数值的有序性。getMaxKey和getMinKey操作可以通过返回链表头尾节点的任意一个键来实现。
时间复杂度:
O(1)
空间复杂度:
O(n)
代码细节讲解
🦆
在`inc`和`dec`操作中,如何确保每次插入或删除节点后,链表仍然维持有序状态?
▷🦆
为什么`getMaxKey`和`getMinKey`操作选择返回链表头尾节点的任意一个键,而不是所有具有最大或最小计数值的键?
▷🦆
在`dec`方法中,如果当前key的计数值为1,直接删除key后,是否还需要对链表中的节点进行其他更新处理?
▷🦆
如果在多线程环境下使用此数据结构,当前的实现是否安全?如果不是,需要如何修改才能保证线程安全?
▷