432. All O`one Data Structure
09/18/2021
Design a data structure to store the strings' count with the ability to return the strings with minimum and maximum counts.
Implement the AllOne
class:
AllOne()
Initializes the object of the data structure.inc(String key)
Increments the count of the stringkey
by1
. Ifkey
does not exist in the data structure, insert it with count1
.dec(String key)
Decrements the count of the stringkey
by1
. If the count ofkey
is0
after the decrement, remove it from the data structure. It is guaranteed thatkey
exists in the data structure before the decrement.getMaxKey()
Returns one of the keys with the maximal count. If no element exists, return an empty string""
.getMinKey()
Returns one of the keys with the minimum count. If no element exists, return an empty string""
.
Example 1:
Input
["AllOne", "inc", "inc", "getMaxKey", "getMinKey", "inc", "getMaxKey", "getMinKey"]
[[], ["hello"], ["hello"], [], [], ["leet"], [], []]
Output
[null, null, null, "hello", "hello", null, "hello", "leet"]
Explanation
AllOne allOne = new AllOne();
allOne.inc("hello");
allOne.inc("hello");
allOne.getMaxKey(); // return "hello"
allOne.getMinKey(); // return "hello"
allOne.inc("leet");
allOne.getMaxKey(); // return "hello"
allOne.getMinKey(); // return "leet"
Constraints:
1 <= key.length <= 10
key
consists of lowercase English letters.It is guaranteed that for each call to
dec
,key
is existing in the data structure.At most
5 * 104
calls will be made toinc
,dec
,getMaxKey
, andgetMinKey
.
Basic Idea:
这道题目和LFU Cache有些类似,要实现 O(1)
的时间复杂度,可以使用一个doubly linked list来维持每个freq的顺序,当count +1 或者 -1 的时候,将key从原本freq对应的node中挪到前面或者后面的node中。Min和Max可以在head和tail中得到。和LFU那道题不同的是,这里不需要在相同freq的node之间实现LRU,逻辑上稍微简单一些。
Java Code:
class AllOne {
private static class Node {
int count;
Node prev;
Node next;
Set<String> keys = new HashSet<>();
public Node(int count) {
this.count = count;
}
}
private Node head;
private Node tail;
private Map<Integer, Node> countNodeMap = new HashMap<>();
private Map<String, Integer> countKeyMap = new HashMap<>();
/** Initialize your data structure here. */
public AllOne() {
head = new Node(0);
tail = new Node(-1);
head.next = tail;
tail.prev = head;
}
/** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */
public void inc(String key) {
if (!countKeyMap.containsKey(key)) {
// 如果之前不存在,则插到最前面count=1的node中
ensureExistNext(head);
head.next.keys.add(key);
countKeyMap.put(key, 1);
} else {
// 如果存在,则删掉旧的,加入old count+1 的node中
int prevCount = countKeyMap.get(key);
Node prevNode = countNodeMap.get(prevCount);
ensureExistNext(prevNode);
prevNode.next.keys.add(key);
prevNode.keys.remove(key);
if (prevNode.keys.isEmpty()) {
deleteNode(prevNode);
}
countKeyMap.put(key, prevCount + 1);
}
}
/** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
public void dec(String key) {
int prevCount = countKeyMap.get(key);
Node prevNode = countNodeMap.get(prevCount);
if (prevCount > 1) {
ensureExistPrev(prevNode);
prevNode.prev.keys.add(key);
countKeyMap.put(key, prevCount - 1);
} else {
// 如果减为0,则需要删掉key
countKeyMap.remove(key);
}
prevNode.keys.remove(key);
if (prevNode.keys.isEmpty()) {
deleteNode(prevNode);
}
}
/** Returns one of the keys with maximal value. */
public String getMaxKey() {
if (head.next == tail) {
return "";
}
return tail.prev.keys.iterator().next();
}
/** Returns one of the keys with Minimal value. */
public String getMinKey() {
if (head.next == tail) {
return "";
}
return head.next.keys.iterator().next();
}
// ensure previous node with count curr.count-1 exist
private void ensureExistPrev(Node curr) {
if (curr.prev == head || curr.prev.count != curr.count - 1) {
Node newNode = new Node(curr.count - 1);
newNode.next = curr;
newNode.prev = curr.prev;
curr.prev.next = newNode;
curr.prev = newNode;
countNodeMap.put(newNode.count, newNode);
}
}
// ensure next node with count curr.count+1 exist
private void ensureExistNext(Node curr) {
if (curr.next == tail
|| curr.next.count != curr.count + 1) {
Node newNode = new Node(curr.count + 1);
newNode.next = curr.next;
newNode.prev = curr;
curr.next.prev = newNode;
curr.next = newNode;
countNodeMap.put(newNode.count, newNode);
}
}
private void deleteNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
countNodeMap.remove(node.count);
}
}
/**
* Your AllOne object will be instantiated and called as such:
* AllOne obj = new AllOne();
* obj.inc(key);
* obj.dec(key);
* String param_3 = obj.getMaxKey();
* String param_4 = obj.getMinKey();
*/
Last updated