Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
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 string key by 1. If key does not exist in the data structure, insert it with count 1.dec(String key) Decrements the count of the string key by 1. If the count of key is 0 after the decrement, remove it from the data structure. It is guaranteed that key 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 "".Note that each function must run in O(1) average time complexity.
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 <= 10key consists of lowercase English letters.dec, key is existing in the data structure.5 * 104 calls will be made to inc, dec, getMaxKey, and getMinKey.Problem summary: 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 string key by 1. If key does not exist in the data structure, insert it with count 1. dec(String key) Decrements the count of the string key by 1. If the count of key is 0 after the decrement, remove it from the data structure. It is guaranteed that key 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 "". Note that each function must run in O(1) average time complexity.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map · Linked List · Design
["AllOne","inc","inc","getMaxKey","getMinKey","inc","getMaxKey","getMinKey"] [[],["hello"],["hello"],[],[],["leet"],[],[]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #432: All O`one Data Structure
class AllOne {
Node root = new Node();
Map<String, Node> nodes = new HashMap<>();
public AllOne() {
root.next = root;
root.prev = root;
}
public void inc(String key) {
if (!nodes.containsKey(key)) {
if (root.next == root || root.next.cnt > 1) {
nodes.put(key, root.insert(new Node(key, 1)));
} else {
root.next.keys.add(key);
nodes.put(key, root.next);
}
} else {
Node curr = nodes.get(key);
Node next = curr.next;
if (next == root || next.cnt > curr.cnt + 1) {
nodes.put(key, curr.insert(new Node(key, curr.cnt + 1)));
} else {
next.keys.add(key);
nodes.put(key, next);
}
curr.keys.remove(key);
if (curr.keys.isEmpty()) {
curr.remove();
}
}
}
public void dec(String key) {
Node curr = nodes.get(key);
if (curr.cnt == 1) {
nodes.remove(key);
} else {
Node prev = curr.prev;
if (prev == root || prev.cnt < curr.cnt - 1) {
nodes.put(key, prev.insert(new Node(key, curr.cnt - 1)));
} else {
prev.keys.add(key);
nodes.put(key, prev);
}
}
curr.keys.remove(key);
if (curr.keys.isEmpty()) {
curr.remove();
}
}
public String getMaxKey() {
return root.prev.keys.iterator().next();
}
public String getMinKey() {
return root.next.keys.iterator().next();
}
}
class Node {
Node prev;
Node next;
int cnt;
Set<String> keys = new HashSet<>();
public Node() {
this("", 0);
}
public Node(String key, int cnt) {
this.cnt = cnt;
keys.add(key);
}
public Node insert(Node node) {
node.prev = this;
node.next = this.next;
node.prev.next = node;
node.next.prev = node;
return node;
}
public void remove() {
this.prev.next = this.next;
this.next.prev = this.prev;
}
}
/**
* 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();
*/
// Accepted solution for LeetCode #432: All O`one Data Structure
// Auto-generated Go example from java.
func exampleSolution() {
}
// Reference (java):
// // Accepted solution for LeetCode #432: All O`one Data Structure
// class AllOne {
// Node root = new Node();
// Map<String, Node> nodes = new HashMap<>();
//
// public AllOne() {
// root.next = root;
// root.prev = root;
// }
//
// public void inc(String key) {
// if (!nodes.containsKey(key)) {
// if (root.next == root || root.next.cnt > 1) {
// nodes.put(key, root.insert(new Node(key, 1)));
// } else {
// root.next.keys.add(key);
// nodes.put(key, root.next);
// }
// } else {
// Node curr = nodes.get(key);
// Node next = curr.next;
// if (next == root || next.cnt > curr.cnt + 1) {
// nodes.put(key, curr.insert(new Node(key, curr.cnt + 1)));
// } else {
// next.keys.add(key);
// nodes.put(key, next);
// }
// curr.keys.remove(key);
// if (curr.keys.isEmpty()) {
// curr.remove();
// }
// }
// }
//
// public void dec(String key) {
// Node curr = nodes.get(key);
// if (curr.cnt == 1) {
// nodes.remove(key);
// } else {
// Node prev = curr.prev;
// if (prev == root || prev.cnt < curr.cnt - 1) {
// nodes.put(key, prev.insert(new Node(key, curr.cnt - 1)));
// } else {
// prev.keys.add(key);
// nodes.put(key, prev);
// }
// }
//
// curr.keys.remove(key);
// if (curr.keys.isEmpty()) {
// curr.remove();
// }
// }
//
// public String getMaxKey() {
// return root.prev.keys.iterator().next();
// }
//
// public String getMinKey() {
// return root.next.keys.iterator().next();
// }
// }
//
// class Node {
// Node prev;
// Node next;
// int cnt;
// Set<String> keys = new HashSet<>();
//
// public Node() {
// this("", 0);
// }
//
// public Node(String key, int cnt) {
// this.cnt = cnt;
// keys.add(key);
// }
//
// public Node insert(Node node) {
// node.prev = this;
// node.next = this.next;
// node.prev.next = node;
// node.next.prev = node;
// return node;
// }
//
// public void remove() {
// this.prev.next = this.next;
// this.next.prev = this.prev;
// }
// }
//
// /**
// * 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();
// */
# Accepted solution for LeetCode #432: All O`one Data Structure
class Node:
def __init__(self, key='', cnt=0):
self.prev = None
self.next = None
self.cnt = cnt
self.keys = {key}
def insert(self, node):
node.prev = self
node.next = self.next
node.prev.next = node
node.next.prev = node
return node
def remove(self):
self.prev.next = self.next
self.next.prev = self.prev
class AllOne:
def __init__(self):
self.root = Node()
self.root.next = self.root
self.root.prev = self.root
self.nodes = {}
def inc(self, key: str) -> None:
root, nodes = self.root, self.nodes
if key not in nodes:
if root.next == root or root.next.cnt > 1:
nodes[key] = root.insert(Node(key, 1))
else:
root.next.keys.add(key)
nodes[key] = root.next
else:
curr = nodes[key]
next = curr.next
if next == root or next.cnt > curr.cnt + 1:
nodes[key] = curr.insert(Node(key, curr.cnt + 1))
else:
next.keys.add(key)
nodes[key] = next
curr.keys.discard(key)
if not curr.keys:
curr.remove()
def dec(self, key: str) -> None:
root, nodes = self.root, self.nodes
curr = nodes[key]
if curr.cnt == 1:
nodes.pop(key)
else:
prev = curr.prev
if prev == root or prev.cnt < curr.cnt - 1:
nodes[key] = prev.insert(Node(key, curr.cnt - 1))
else:
prev.keys.add(key)
nodes[key] = prev
curr.keys.discard(key)
if not curr.keys:
curr.remove()
def getMaxKey(self) -> str:
return next(iter(self.root.prev.keys))
def getMinKey(self) -> str:
return next(iter(self.root.next.keys))
# Your AllOne object will be instantiated and called as such:
# obj = AllOne()
# obj.inc(key)
# obj.dec(key)
# param_3 = obj.getMaxKey()
# param_4 = obj.getMinKey()
// Accepted solution for LeetCode #432: All O`one Data Structure
use std::collections::HashMap;
struct AllOne {
dict: HashMap<String, usize>,
}
impl AllOne {
fn new() -> Self {
let dict = HashMap::new();
AllOne { dict }
}
fn inc(&mut self, key: String) {
self.dict.entry(key).and_modify(|v| *v += 1).or_insert(1);
}
fn dec(&mut self, key: String) {
if !self.dict.contains_key(&key) {
return;
}
if self.dict[&key] == 1 {
self.dict.remove(&key);
} else {
*self.dict.get_mut(&key).unwrap() -= 1;
}
}
fn get_max_key(&mut self) -> String {
self.dict
.iter()
.max_by_key(|(_, &v)| v)
.unwrap_or((&String::from(""), &0))
.0
.to_string()
}
fn get_min_key(&mut self) -> String {
self.dict
.iter()
.min_by_key(|(_, &v)| v)
.unwrap_or((&String::from(""), &0))
.0
.to_string()
}
}
#[test]
fn test() {
let mut obj = AllOne::new();
obj.inc("abc".to_string());
assert_eq!(obj.get_max_key(), "abc".to_string());
}
// Accepted solution for LeetCode #432: All O`one Data Structure
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #432: All O`one Data Structure
// class AllOne {
// Node root = new Node();
// Map<String, Node> nodes = new HashMap<>();
//
// public AllOne() {
// root.next = root;
// root.prev = root;
// }
//
// public void inc(String key) {
// if (!nodes.containsKey(key)) {
// if (root.next == root || root.next.cnt > 1) {
// nodes.put(key, root.insert(new Node(key, 1)));
// } else {
// root.next.keys.add(key);
// nodes.put(key, root.next);
// }
// } else {
// Node curr = nodes.get(key);
// Node next = curr.next;
// if (next == root || next.cnt > curr.cnt + 1) {
// nodes.put(key, curr.insert(new Node(key, curr.cnt + 1)));
// } else {
// next.keys.add(key);
// nodes.put(key, next);
// }
// curr.keys.remove(key);
// if (curr.keys.isEmpty()) {
// curr.remove();
// }
// }
// }
//
// public void dec(String key) {
// Node curr = nodes.get(key);
// if (curr.cnt == 1) {
// nodes.remove(key);
// } else {
// Node prev = curr.prev;
// if (prev == root || prev.cnt < curr.cnt - 1) {
// nodes.put(key, prev.insert(new Node(key, curr.cnt - 1)));
// } else {
// prev.keys.add(key);
// nodes.put(key, prev);
// }
// }
//
// curr.keys.remove(key);
// if (curr.keys.isEmpty()) {
// curr.remove();
// }
// }
//
// public String getMaxKey() {
// return root.prev.keys.iterator().next();
// }
//
// public String getMinKey() {
// return root.next.keys.iterator().next();
// }
// }
//
// class Node {
// Node prev;
// Node next;
// int cnt;
// Set<String> keys = new HashSet<>();
//
// public Node() {
// this("", 0);
// }
//
// public Node(String key, int cnt) {
// this.cnt = cnt;
// keys.add(key);
// }
//
// public Node insert(Node node) {
// node.prev = this;
// node.next = this.next;
// node.prev.next = node;
// node.next.prev = node;
// return node;
// }
//
// public void remove() {
// this.prev.next = this.next;
// this.next.prev = this.prev;
// }
// }
//
// /**
// * 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();
// */
Use this to step through a reusable interview workflow for this problem.
Copy all n nodes into an array (O(n) time and space), then use array indexing for random access. Operations like reversal or middle-finding become trivial with indices, but the O(n) extra space defeats the purpose of using a linked list.
Most linked list operations traverse the list once (O(n)) and re-wire pointers in-place (O(1) extra space). The brute force often copies nodes to an array to enable random access, costing O(n) space. In-place pointer manipulation eliminates that.
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Pointer updates overwrite references before they are saved.
Usually fails on: List becomes disconnected mid-operation.
Fix: Store next pointers first and use a dummy head for safer joins.