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 code snippet implementing the optimal approach to copy a list with random pointers. Given the input list:
Node1(val=1) -> Node2(val=2) -> Node3(val=3), where Node1.random = Node3, Node2.random = Node1, Node3.random = null.
After Step 2 (assigning random pointers), what is the value of copy_curr.random.val when copy_curr points to the copied node of Node2?
easy
A. None (null)
B. 3
C. 1
D. 2

Solution

  1. Step 1: Trace Step 1 (interleaving nodes)

    Original list: 1 -> 2 -> 3 becomes 1 -> 1' -> 2 -> 2' -> 3 -> 3'.
  2. Step 2: Trace Step 2 (assign random pointers)

    Node2.random = Node1, so copied Node2 (2') random = Node1.next = 1'. Thus, copy_curr.random.val = 1.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    copy_curr.random points to copied Node1 with val=1 [OK]
Hint: Copied node's random points to original.random.next [OK]
Common Mistakes:
  • Confusing original and copied nodes when assigning random pointers
  • Off-by-one errors in stepping through interleaved list
  • Assuming random pointer points to original node's val
2. Given the following code snippet for the optimal UndergroundSystem implementation, what is the value of travel_time when checkOut(1, "Leyton", 20) is called after checkIn(1, "Paradise", 3) and checkIn(2, "Leyton", 10)?
class Node:
    def __init__(self, id, station, time):
        self.id = id
        self.station = station
        self.time = time
        self.next = None

class UndergroundSystem:
    def __init__(self):
        self.head = None
        self.route_data = {}

    def checkIn(self, id: int, stationName: str, t: int) -> None:
        new_node = Node(id, stationName, t)
        new_node.next = self.head
        self.head = new_node

    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
                if prev:
                    prev.next = curr.next
                else:
                    self.head = curr.next
                return
            prev = curr
            curr = curr.next
easy
A. 10
B. 17
C. 7
D. 20

Solution

  1. Step 1: Trace checkIn calls

    First, checkIn(1, "Paradise", 3) adds Node(id=1, station="Paradise", time=3) at head. Then checkIn(2, "Leyton", 10) adds Node(id=2, station="Leyton", time=10) at head.
  2. Step 2: Trace checkOut(1, "Leyton", 20)

    Traverse linked list from head: Node with id=2 (not match), then Node with id=1 (match). travel_time = 20 - 3 = 17.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-in time for id=1 is 3, checkout time is 20, difference is 17 [OK]
Hint: Subtract check-in time from checkout time for travel_time [OK]
Common Mistakes:
  • Using wrong node's time due to linked list order
3. You are given a doubly linked list where some nodes have a child pointer to another doubly linked list. The child lists can themselves have children, and so on. Which approach best guarantees an in-place flattening of this multilevel list into a single-level doubly linked list without using extra space?
easy
A. Iteratively traverse the list, and whenever a node has a child, splice the child list in place by connecting the child's tail to the node's next, updating pointers accordingly.
B. Use a recursive depth-first traversal that returns the tail of each flattened child list to connect nodes.
C. Apply a greedy approach that always appends child lists at the end of the main list after full traversal.
D. Use dynamic programming to store intermediate flattened sublists and merge them bottom-up.

Solution

  1. Step 1: Understand the problem constraints

    The problem requires flattening a multilevel doubly linked list in-place without extra space.
  2. Step 2: Identify the approach that guarantees in-place flattening without extra space

    Using a recursive depth-first traversal that returns the tail of each flattened child list allows splicing child lists correctly and efficiently without extra data structures.
  3. Step 3: Evaluate other options

    Iterative splicing (Iteratively traverse the list, and whenever a node has a child, splice the child list in place by connecting the child's tail to the node's next, updating pointers accordingly.) can cause repeated tail searches leading to O(n^2) time and is less straightforward. Greedy appending (Apply a greedy approach that always appends child lists at the end of the main list after full traversal.) fails to maintain order. Dynamic programming (Use dynamic programming to store intermediate flattened sublists and merge them bottom-up.) is not applicable.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Recursive DFS approach is standard and efficient for in-place flattening [OK]
Hint: Recursive DFS returns tail to connect lists efficiently [OK]
Common Mistakes:
  • Assuming iterative splicing is always best despite complexity
  • Appending child lists only at the end
  • Using DP which is not applicable here
4. What is the time complexity of the get operation in the optimal SnapshotArray implementation that uses a hash map per index with binary search, assuming m snapshots and k set operations?
medium
A. O(1) because values are stored directly per index and snap_id
B. O(k) because get must scan all set operations for the index
C. O(m) because linear search is needed over all snapshots
D. O(log m) because binary search is performed on at most m entries per index

Solution

  1. Step 1: Identify data structure for get

    Each index stores a sorted list of (snap_id, value) pairs, with length up to number of set operations k but bounded by m snapshots.
  2. Step 2: Analyze get operation

    Get uses binary search on this list to find the value for the requested snap_id, which takes O(log m) time since at most one entry per snapshot per index.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Binary search on sorted snapshot list yields O(log m) [OK]
Hint: Binary search on snapshots per index yields O(log m) get time [OK]
Common Mistakes:
  • Assuming get is O(1) due to direct indexing
  • Confusing linear search with binary search
5. Suppose the UndergroundSystem is extended to allow multiple check-ins by the same passenger without requiring a check-out first (i.e., passengers can start multiple trips before finishing any). Which modification to the data structure or algorithm is necessary to handle this correctly?
hard
A. Keep a stack of check-in nodes per passenger id to track multiple active trips.
B. Use a hash map from passenger id to a single check-in node, overwriting previous check-ins.
C. Store all check-ins in a linked list and traverse fully on each checkOut to find the earliest unmatched check-in.
D. Disallow multiple check-ins by the same passenger to avoid complexity.

Solution

  1. Step 1: Understand the problem extension

    Passengers can have multiple active trips simultaneously, so a single check-in per id is insufficient.
  2. Step 2: Choose data structure to track multiple check-ins

    A stack per passenger id allows tracking multiple check-ins in order, enabling correct matching on checkOut.
  3. Step 3: Why other options fail

    Overwriting check-ins loses data (A). Traversing full list each time (C) is inefficient. Disallowing multiple check-ins (D) is not a solution.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Stack per id supports multiple active trips correctly [OK]
Hint: Stack per id tracks multiple active check-ins [OK]
Common Mistakes:
  • Overwriting check-in info loses data
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