题目来源:https://leetcode-cn.com/problems/all-oone-data-structure/
大致题意:
设计一个类 AllOne,包含以下方法
- 无参构造器
- inc(String key) key 对应的字符串计数 +1
- dec(String key) key 对应的字符串计数 -1,若为 0 则删除该字符串(题目保证 -1 时一定有对应的字符串)
- getMaxKey() 返回任意一个计数最大的字符串。如果没有元素存在,返回一个空字符串 “”
- getMinKey() 返回任意一个计数最小的字符串。如果没有元素存在,返回一个空字符串 “”
要求所有操作的时间复杂度为 O(1)
思路
使用一个双向链表存计数以及对应的字符串可以集合,再用一个哈希表标记字符串所在的链表节点
这样每次调用方法 inc(key) 时,按照如下规则:
- 先判断哈希表中是否有该 key 对应的节点,若有,则获取对应计数,然后将 key 放入计数 +1 对应的节点中(若该节点没有则创建),并在原节点的字符串集合中删除 key(若删除后集合为空,则移除该节点);若没有则插入计数 1 对应的链表节点(若该节点没有则先创建)中。最后在哈希表中更新 key 对应节点
调用方法 dec(key) 时,按照如下规则:
- 先获取对应计数,然后判断计数是否为 1,若为 1 直接从哈希表删除 key 对应的键值对;若不为 1,将 key 放入计数 -1 对应的节点中(若该节点没有则创建)
- 之后在原节点的字符串集合中删除 key(若删除后集合为空,则移除该节点)
调用方法 getMaxKey() 直接返回最后一个节点
调用方法 getMinKey() 直接返回第一个节点
public class AllOne {
Map<String, Node> map;
Node root; // 根节点
public AllOne() {
root = new Node();
root.next = root;
root.pre = root;
map = new HashMap<>();
}
public void inc(String key) {
// 若哈希表中有当前字符串,获取所在节点和已有次数
if (map.containsKey(key)) {
Node node = map.get(key);
int count = node.count + 1;
// 更新 key 所在节点
updateInc(key, node, count);
// 从原节点的字符串集合中删去 key
node.keys.remove(key);
// 若原节点对应的字符串为空,删除节点
if (node.keys.isEmpty()) {
node.remove();
}
} else { // 哈希表中没有该字符串,插入
updateInc(key, root, 1);
}
}
public void dec(String key) {
// 获取对应节点
Node node = map.get(key);
int count = node.count - 1;
// 更新后计数为 0,从哈希表中删除
if (count == 0) {
map.remove(key);
} else { // 否则更新 key 所在节点
updateDes(key, node, count);
}
// 从原节点中删除 key
node.keys.remove(key);
if (node.keys.isEmpty()) {
node.remove();
}
}
public String getMaxKey() {
return root.pre == root ? "" : root.pre.keys.iterator().next();
}
public String getMinKey() {
return root.next == root ? "" : root.next.keys.iterator().next();
}
// 更新增加到 count 次的字符串 key 所在的节点,node 为原节点(若之前没在链表中,node 为根节点)
public void updateInc(String key, Node node, int count) {
if (node.next == root || node.next.count > count) {
map.put(key, node.insert(new Node(count, key)));
} else {
node.next.keys.add(key);
map.put(key, node.next);
}
}
// 更新减少到 count 次的字符串 key 所在节点,node 为原节点
public void updateDes(String key, Node node, int count) {
if (node.pre == root || node.pre.count < count) {
map.put(key, node.pre.insert(new Node(count, key)));
} else {
node.pre.keys.add(key);
map.put(key, node.pre);
}
}
class Node {
int count;
Set<String> keys;
Node pre;
Node next;
public Node() {
}
public Node(int count, String key) {
this.count = count;
keys = new HashSet<>();
keys.add(key);
}
// 在当前节点后插入一个节点
public Node insert(Node node) {
node.pre = this;
node.next = this.next;
node.pre.next = node;
node.next.pre = node;
return node;
}
// 删除当前节点
public void remove() {
this.pre.next = this.next;
this.next.pre = this.pre;
}
}
}