Bird
Raised Fist0
Interview Prepcustom-data-structureshardAmazonGoogle

All O(1) Data Structure (Max/Min Count)

Choose your preparation mode4 modes available
</>
IDE
def inc(self, key: str) -> None; def dec(self, key: str) -> None; def getMaxKey(self) -> str; def getMinKey(self) -> strpublic void inc(String key); public void dec(String key); public String getMaxKey(); public String getMinKey()void inc(string key); void dec(string key); string getMaxKey(); string getMinKey()function inc(key); function dec(key); function getMaxKey(); function getMinKey()
class AllOne:
    def __init__(self):
        pass
    def inc(self, key: str) -> None:
        pass
    def dec(self, key: str) -> None:
        pass
    def getMaxKey(self) -> str:
        pass
    def getMinKey(self) -> str:
        pass
class AllOne {
    public AllOne() {
    }
    public void inc(String key) {
    }
    public void dec(String key) {
    }
    public String getMaxKey() {
        return "";
    }
    public String getMinKey() {
        return "";
    }
}
#include <string>
using namespace std;

class AllOne {
public:
    AllOne() {
    }
    void inc(string key) {
    }
    void dec(string key) {
    }
    string getMaxKey() {
        return "";
    }
    string getMinKey() {
        return "";
    }
};
class AllOne {
    constructor() {
    }
    inc(key) {
    }
    dec(key) {
    }
    getMaxKey() {
        return "";
    }
    getMinKey() {
        return "";
    }
}
Coming soon
0/10
Common Bugs to Avoid
Wrong: "" for getMaxKey/getMinKey when keys existNot updating or maintaining counts and buckets after inc/dec operations.✅ Ensure keys are added to count buckets on inc and removed or moved on dec; update max/min pointers accordingly.
Wrong: Returning stale max/min keys after decrement removes keysFailing to remove keys from data structure when count reaches zero.✅ Delete keys from counts and buckets when count reaches zero and update max/min pointers.
Wrong: Wrong max/min keys due to greedy selection ignoring countsSelecting keys based on insertion order or last updated rather than counts.✅ Always select keys from buckets with max or min counts, not based on insertion order.
Wrong: TLE on large inputUsing linear scans to find max/min keys instead of O(1) data structure.✅ Implement doubly linked list with hash maps to maintain buckets of keys by counts for O(1) operations.
✓
Test Cases
t1_01basic
Input[{"op":"inc","arg":"hello"},{"op":"inc","arg":"hello"},{"op":"getMaxKey"},{"op":"getMinKey"},{"op":"dec","arg":"hello"},{"op":"getMaxKey"},{"op":"getMinKey"}]
Expected["hello","hello","hello","hello"]

After two increments, 'hello' count is 2, so max and min keys are 'hello'. After decrement, count is 1, so max and min keys remain 'hello'.

t1_02basic
Input[{"op":"inc","arg":"apple"},{"op":"inc","arg":"banana"},{"op":"inc","arg":"apple"},{"op":"getMaxKey"},{"op":"getMinKey"},{"op":"dec","arg":"apple"},{"op":"getMaxKey"},{"op":"getMinKey"}]
Expected["apple","banana","apple","banana"]

After increments, 'apple' count is 2, 'banana' count is 1. Max key is 'apple', min key is 'banana'. After decrement of 'apple', both have count 1, so max and min keys can be either; here max is 'apple' and min is 'banana'.

t2_01edge
Input[{"op":"getMaxKey"},{"op":"getMinKey"}]
Expected["",""]

Empty data structure returns empty string for max and min keys.

t2_02edge
Input[{"op":"inc","arg":"singlekey"},{"op":"getMaxKey"},{"op":"getMinKey"}]
Expected["singlekey","singlekey"]

Single key incremented once; max and min keys are the same key.

t2_03edge
Input[{"op":"inc","arg":"key1"},{"op":"dec","arg":"key1"},{"op":"getMaxKey"},{"op":"getMinKey"}]
Expected["",""]

Decrementing a key with count 1 removes it; data structure becomes empty, so max and min keys are empty strings.

t2_04edge
Input[{"op":"inc","arg":"same"},{"op":"inc","arg":"same"},{"op":"inc","arg":"same"},{"op":"getMaxKey"},{"op":"getMinKey"}]
Expected["same","same"]

All elements identical; max and min keys are the same key.

t3_01corner
Input[{"op":"inc","arg":"a"},{"op":"inc","arg":"b"},{"op":"inc","arg":"c"},{"op":"inc","arg":"a"},{"op":"inc","arg":"b"},{"op":"dec","arg":"a"},{"op":"getMaxKey"},{"op":"getMinKey"}]
Expected["b","c"]

After operations, 'b' has count 2, 'a' and 'c' have count 1. Max key is 'b', min key is 'c'.

t3_02corner
Input[{"op":"inc","arg":"x"},{"op":"inc","arg":"x"},{"op":"dec","arg":"x"},{"op":"dec","arg":"x"},{"op":"getMaxKey"},{"op":"getMinKey"}]
Expected["",""]

Increment twice then decrement twice removes key 'x'; data structure empty, so max and min keys are empty strings.

t3_03corner
Input[{"op":"inc","arg":"alpha"},{"op":"inc","arg":"beta"},{"op":"inc","arg":"gamma"},{"op":"inc","arg":"beta"},{"op":"inc","arg":"gamma"},{"op":"inc","arg":"gamma"},{"op":"getMaxKey"},{"op":"getMinKey"}]
Expected["gamma","alpha"]

'gamma' count is 3, 'beta' count is 2, 'alpha' count is 1. Max key is 'gamma', min key is 'alpha'.

t4_01performance
Input[{"op":"inc","arg":"k0"},{"op":"inc","arg":"k1"},{"op":"inc","arg":"k2"},{"op":"inc","arg":"k3"},{"op":"inc","arg":"k4"},{"op":"inc","arg":"k5"},{"op":"inc","arg":"k6"},{"op":"inc","arg":"k7"},{"op":"inc","arg":"k8"},{"op":"inc","arg":"k9"},{"op":"inc","arg":"k10"},{"op":"inc","arg":"k11"},{"op":"inc","arg":"k12"},{"op":"inc","arg":"k13"},{"op":"inc","arg":"k14"},{"op":"inc","arg":"k15"},{"op":"inc","arg":"k16"},{"op":"inc","arg":"k17"},{"op":"inc","arg":"k18"},{"op":"inc","arg":"k19"},{"op":"inc","arg":"k20"},{"op":"inc","arg":"k21"},{"op":"inc","arg":"k22"},{"op":"inc","arg":"k23"},{"op":"inc","arg":"k24"},{"op":"inc","arg":"k25"},{"op":"inc","arg":"k26"},{"op":"inc","arg":"k27"},{"op":"inc","arg":"k28"},{"op":"inc","arg":"k29"},{"op":"inc","arg":"k30"},{"op":"inc","arg":"k31"},{"op":"inc","arg":"k32"},{"op":"inc","arg":"k33"},{"op":"inc","arg":"k34"},{"op":"inc","arg":"k35"},{"op":"inc","arg":"k36"},{"op":"inc","arg":"k37"},{"op":"inc","arg":"k38"},{"op":"inc","arg":"k39"},{"op":"inc","arg":"k40"},{"op":"inc","arg":"k41"},{"op":"inc","arg":"k42"},{"op":"inc","arg":"k43"},{"op":"inc","arg":"k44"},{"op":"inc","arg":"k45"},{"op":"inc","arg":"k46"},{"op":"inc","arg":"k47"},{"op":"inc","arg":"k48"},{"op":"inc","arg":"k49"},{"op":"dec","arg":"k0"},{"op":"dec","arg":"k1"},{"op":"dec","arg":"k2"},{"op":"dec","arg":"k3"},{"op":"dec","arg":"k4"},{"op":"dec","arg":"k5"},{"op":"dec","arg":"k6"},{"op":"dec","arg":"k7"},{"op":"dec","arg":"k8"},{"op":"dec","arg":"k9"},{"op":"dec","arg":"k10"},{"op":"dec","arg":"k11"},{"op":"dec","arg":"k12"},{"op":"dec","arg":"k13"},{"op":"dec","arg":"k14"},{"op":"dec","arg":"k15"},{"op":"dec","arg":"k16"},{"op":"dec","arg":"k17"},{"op":"dec","arg":"k18"},{"op":"dec","arg":"k19"},{"op":"dec","arg":"k20"},{"op":"dec","arg":"k21"},{"op":"dec","arg":"k22"},{"op":"dec","arg":"k23"},{"op":"dec","arg":"k24"},{"op":"dec","arg":"k25"},{"op":"dec","arg":"k26"},{"op":"dec","arg":"k27"},{"op":"dec","arg":"k28"},{"op":"dec","arg":"k29"},{"op":"dec","arg":"k30"},{"op":"dec","arg":"k31"},{"op":"dec","arg":"k32"},{"op":"dec","arg":"k33"},{"op":"dec","arg":"k34"},{"op":"dec","arg":"k35"},{"op":"dec","arg":"k36"},{"op":"dec","arg":"k37"},{"op":"dec","arg":"k38"},{"op":"dec","arg":"k39"},{"op":"dec","arg":"k40"},{"op":"dec","arg":"k41"},{"op":"dec","arg":"k42"},{"op":"dec","arg":"k43"},{"op":"dec","arg":"k44"},{"op":"dec","arg":"k45"},{"op":"dec","arg":"k46"},{"op":"dec","arg":"k47"},{"op":"dec","arg":"k48"},{"op":"dec","arg":"k49"}]
⏱ Performance - must finish in 2000ms

n=100 operations, O(1) per operation required to pass within 2s

Practice

(1/5)
1. Consider the following Python code implementing the optimal CustomStack with lazy increments. After executing the sequence of operations below, what is the output of the last pop() call?
cs = CustomStack(3)
cs.push(1)
cs.push(2)
cs.increment(2, 5)
print(cs.pop())
easy
A. 2
B. 7
C. 6
D. 1

Solution

  1. Step 1: Trace the increment operation

    After pushing 1 and 2, stack = [1, 2], inc = [0, 0, 0]. increment(2, 5) sets inc[1] += 5 -> inc = [0, 5, 0].
  2. Step 2: Trace the pop operation

    pop() removes top element at index 1 (value 2). Since i=1 > 0, inc[0] += inc[1] -> inc[0] = 0 + 5 = 5. Result = stack.pop() + inc[1] = 2 + 5 = 7. inc[1] reset to 0.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Increment applied lazily, pop returns 7 [OK]
Hint: Increment applies to inc array index k-1, added on pop [OK]
Common Mistakes:
  • Forgetting to propagate increments down on pop
  • Adding increments directly to popped element only
2. Consider the following buggy code snippet for the optimal UndergroundSystem implementation. What is the subtle bug causing incorrect behavior or memory leaks?
def checkOut(self, id: int, stationName: str, t: int) -> None:
    prev = None
    curr = self.head
    while curr:
        if curr.id == id:
            travel_time = t - curr.time
            route = (curr.station, stationName)
            if route not in self.route_data:
                self.route_data[route] = [0, 0]
            self.route_data[route][0] += travel_time
            self.route_data[route][1] += 1
            # BUG: Missing removal of check-in node
            return
        prev = curr
        curr = curr.next
medium
A. Line with 'if curr.id == id:' missing a check for None
B. Incorrect initialization of route_data dictionary
C. Missing code to remove the matched node from the linked list after processing
D. Not updating the head pointer after checkOut

Solution

  1. Step 1: Identify missing removal

    The code does not remove the matched check-in node from the linked list, causing memory leaks and repeated matches.
  2. Step 2: Consequences of missing removal

    Without removal, subsequent checkOut calls for the same id will find the old node again, causing incorrect data and memory growth.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Removal of node is essential to avoid stale data [OK]
Hint: Check if matched node is removed after checkOut [OK]
Common Mistakes:
  • Forgetting to unlink node after processing
3. Examine the following buggy code snippet for the MedianFinder class. Which line contains the subtle bug that can cause incorrect median calculation?
medium
A. Condition 'if self.low_size > self.high_size:' for balancing heaps
B. Line that pushes to high heap and increments high_size
C. Line that pushes to low heap and increments low_size
D. Condition 'elif self.high_size > self.low_size + 1:' for balancing heaps

Solution

  1. Step 1: Understand balancing condition

    The heaps must be balanced so that their sizes differ by at most 1.
  2. Step 2: Identify incorrect condition

    The condition 'if self.low_size > self.high_size:' moves an element from low to high even when sizes differ by only 1, causing imbalance.
  3. Step 3: Correct condition

    The condition should be 'if self.low_size > self.high_size + 1:' to ensure size difference is more than 1 before balancing.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Balancing condition must check if low_size > high_size + 1 [OK]
Hint: Balance heaps only if size difference > 1 [OK]
Common Mistakes:
  • Balancing heaps too aggressively
  • Incorrect size difference checks
4. What is the average time complexity of the remove operation in the optimized Insert Delete GetRandom O(1) with duplicates data structure that uses a hash map of element to linked list nodes and a list of elements?
medium
A. O(n) because we need to find the element's index in the list before removal.
B. O(log n) due to maintaining the linked list structure during removals.
C. O(1) worst-case because linked list removals and hash map lookups are constant time.
D. O(1) average because the hash map provides direct access to an element's node for removal.

Solution

  1. Step 1: Identify data structure operations

    Removal uses hash map to get a node reference in O(1) average time, then removes node from doubly linked list in O(1).
  2. Step 2: Clarify average vs worst-case

    Hash map lookups are average O(1), but worst-case can degrade. Linked list removal is always O(1). So overall average is O(1).
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Hash map + linked list enables average O(1) removals [OK]
Hint: Hash map + linked list enables average O(1) removals [OK]
Common Mistakes:
  • Confusing average and worst-case complexities
  • Assuming linked list removal is O(log n)
  • Thinking index search is needed
5. What is the time complexity of the divide and conquer approach to the Skyline Problem when given n buildings? Assume merging two skylines of total length m takes O(m) time.
medium
A. O(n²)
B. O(n)
C. O(n log n)
D. O(n * w) where w is the width of the skyline

Solution

  1. Step 1: Identify divide and conquer recurrence

    The algorithm splits buildings into halves recursively, then merges skylines in O(m) time where m is proportional to n.
  2. Step 2: Solve recurrence and analyze merge cost

    Recurrence is T(n) = 2T(n/2) + O(n), which solves to O(n log n). The width w does not affect complexity here.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Divide and conquer with linear merge per level -> O(n log n) [OK]
Hint: Divide and conquer recurrence T(n)=2T(n/2)+O(n) -> O(n log n) [OK]
Common Mistakes:
  • Confusing width w with n
  • Assuming quadratic due to nested loops
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard