Bird
Raised Fist0
Interview Prepcustom-data-structuresmediumAmazonGoogleFacebook

Design Underground System

Choose your preparation mode4 modes available
</>
IDE
def checkIn(self, id: int, stationName: str, t: int) -> None:public void checkIn(int id, String stationName, int t)void checkIn(int id, string stationName, int t)function checkIn(id, stationName, t)
class UndergroundSystem:
    def __init__(self):
        pass
    def checkIn(self, id: int, stationName: str, t: int) -> None:
        pass
    def checkOut(self, id: int, stationName: str, t: int) -> None:
        pass
    def getAverageTime(self, startStation: str, endStation: str) -> float:
        pass
class UndergroundSystem {
    public UndergroundSystem() {
    }
    public void checkIn(int id, String stationName, int t) {
    }
    public void checkOut(int id, String stationName, int t) {
    }
    public double getAverageTime(String startStation, String endStation) {
        return 0.0;
    }
}
#include <string>
using namespace std;
class UndergroundSystem {
public:
    UndergroundSystem() {
    }
    void checkIn(int id, string stationName, int t) {
    }
    void checkOut(int id, string stationName, int t) {
    }
    double getAverageTime(string startStation, string endStation) {
        return 0.0;
    }
};
class UndergroundSystem {
    constructor() {
    }
    checkIn(id, stationName, t) {
    }
    checkOut(id, stationName, t) {
    }
    getAverageTime(startStation, endStation) {
        return 0.0;
    }
}
Coming soon
0/10
Common Bugs to Avoid
Wrong: 0 or error when querying average time with no tripsNot handling empty route data before division✅ Return 0.0 if no trips recorded for the route before dividing
Wrong: Negative or incorrect travel time for same timestamp check-in/outIncorrect time difference calculation or assumptions about time order✅ Compute travel time as checkout time minus checkin time without assumptions
Wrong: Average time incorrect when multiple trips or multiple customersOverwriting check-in data or not accumulating totals correctly✅ Use a map keyed by id for check-ins and accumulate total time and count per route
Wrong: Mixing trips from different routes in average calculationNot separating route keys properly✅ Use (startStation, endStation) as key for totals and counts
Wrong: TLE on large inputsUsing O(n) scan of trips on getAverageTime instead of running totals✅ Maintain running totals and counts per route for O(1) getAverageTime
✓
Test Cases
t1_01basic
Input["checkIn(45, \"Leyton\", 3)","checkOut(45, \"Waterloo\", 15)","getAverageTime(\"Leyton\", \"Waterloo\")"]
Expected"12.0"

Customer 45 checked in at Leyton at time 3 and checked out at Waterloo at time 15, so travel time is 12. Average is 12.0 since only one trip.

t1_02basic
Input["checkIn(10, \"Leyton\", 5)","checkOut(10, \"Waterloo\", 15)","checkIn(20, \"Leyton\", 10)","checkOut(20, \"Waterloo\", 20)","getAverageTime(\"Leyton\", \"Waterloo\")"]
Expected"10.0"

Two customers traveled from Leyton to Waterloo with times 10 and 10, average is 10.0.

t2_01edge
Input["getAverageTime(\"Leyton\", \"Waterloo\")"]
Expected"0.0"

No trips recorded yet, average time should be 0.0.

t2_02edge
Input["checkIn(1, \"A\", 5)","checkOut(1, \"B\", 5)","getAverageTime(\"A\", \"B\")"]
Expected"0.0"

Check-in and check-out at same timestamp results in zero travel time.

t2_03edge
Input["checkIn(1000000, \"Start\", 1)","checkOut(1000000, \"End\", 1000000000)","getAverageTime(\"Start\", \"End\")"]
Expected"999999999.0"

Test with maximum id and maximum time values to ensure no overflow or errors.

t2_04edge
Input["checkIn(1, \"A\", 1)"]
Expected"0.0"

Customer checked in but not checked out yet; average time query should not consider incomplete trips.

t3_01corner
Input["checkIn(1, \"A\", 3)","checkIn(2, \"A\", 8)","checkOut(1, \"B\", 10)","checkOut(2, \"B\", 15)","getAverageTime(\"A\", \"B\")"]
Expected"7.0"

Multiple customers checked in before any check-out; average time should be correct.

t3_02corner
Input["checkIn(1, \"A\", 1)","checkOut(1, \"B\", 5)","checkIn(1, \"A\", 10)","checkOut(1, \"B\", 15)","getAverageTime(\"A\", \"B\")"]
Expected"4.5"

Same customer makes multiple trips; average time should be average of both trips.

t3_03corner
Input["checkIn(1, \"A\", 1)","checkOut(1, \"B\", 5)","checkIn(2, \"A\", 2)","checkOut(2, \"C\", 6)","getAverageTime(\"A\", \"B\")","getAverageTime(\"A\", \"C\")"]
Expected["4.0","4.0"]

Different routes from same start station; averages computed separately.

t4_01performance
Input["checkIn(1, \"S1\", 1)","checkOut(1, \"S2\", 11)","checkIn(2, \"S1\", 2)","checkOut(2, \"S2\", 12)","checkIn(3, \"S1\", 3)","checkOut(3, \"S2\", 13)","checkIn(4, \"S1\", 4)","checkOut(4, \"S2\", 14)","checkIn(5, \"S1\", 5)","checkOut(5, \"S2\", 15)","checkIn(6, \"S1\", 6)","checkOut(6, \"S2\", 16)","checkIn(7, \"S1\", 7)","checkOut(7, \"S2\", 17)","checkIn(8, \"S1\", 8)","checkOut(8, \"S2\", 18)","checkIn(9, \"S1\", 9)","checkOut(9, \"S2\", 19)","checkIn(10, \"S1\", 10)","checkOut(10, \"S2\", 20)","checkIn(11, \"S1\", 11)","checkOut(11, \"S2\", 21)","checkIn(12, \"S1\", 12)","checkOut(12, \"S2\", 22)","checkIn(13, \"S1\", 13)","checkOut(13, \"S2\", 23)","checkIn(14, \"S1\", 14)","checkOut(14, \"S2\", 24)","checkIn(15, \"S1\", 15)","checkOut(15, \"S2\", 25)","checkIn(16, \"S1\", 16)","checkOut(16, \"S2\", 26)","checkIn(17, \"S1\", 17)","checkOut(17, \"S2\", 27)","checkIn(18, \"S1\", 18)","checkOut(18, \"S2\", 28)","checkIn(19, \"S1\", 19)","checkOut(19, \"S2\", 29)","checkIn(20, \"S1\", 20)","checkOut(20, \"S2\", 30)","checkIn(21, \"S1\", 21)","checkOut(21, \"S2\", 31)","checkIn(22, \"S1\", 22)","checkOut(22, \"S2\", 32)","checkIn(23, \"S1\", 23)","checkOut(23, \"S2\", 33)","checkIn(24, \"S1\", 24)","checkOut(24, \"S2\", 34)","checkIn(25, \"S1\", 25)","checkOut(25, \"S2\", 35)","checkIn(26, \"S1\", 26)","checkOut(26, \"S2\", 36)","checkIn(27, \"S1\", 27)","checkOut(27, \"S2\", 37)","checkIn(28, \"S1\", 28)","checkOut(28, \"S2\", 38)","checkIn(29, \"S1\", 29)","checkOut(29, \"S2\", 39)","checkIn(30, \"S1\", 30)","checkOut(30, \"S2\", 40)","checkIn(31, \"S1\", 31)","checkOut(31, \"S2\", 41)","checkIn(32, \"S1\", 32)","checkOut(32, \"S2\", 42)","checkIn(33, \"S1\", 33)","checkOut(33, \"S2\", 43)","checkIn(34, \"S1\", 34)","checkOut(34, \"S2\", 44)","checkIn(35, \"S1\", 35)","checkOut(35, \"S2\", 45)","checkIn(36, \"S1\", 36)","checkOut(36, \"S2\", 46)","checkIn(37, \"S1\", 37)","checkOut(37, \"S2\", 47)","checkIn(38, \"S1\", 38)","checkOut(38, \"S2\", 48)","checkIn(39, \"S1\", 39)","checkOut(39, \"S2\", 49)","checkIn(40, \"S1\", 40)","checkOut(40, \"S2\", 50)","checkIn(41, \"S1\", 41)","checkOut(41, \"S2\", 51)","checkIn(42, \"S1\", 42)","checkOut(42, \"S2\", 52)","checkIn(43, \"S1\", 43)","checkOut(43, \"S2\", 53)","checkIn(44, \"S1\", 44)","checkOut(44, \"S2\", 54)","checkIn(45, \"S1\", 45)","checkOut(45, \"S2\", 55)","checkIn(46, \"S1\", 46)","checkOut(46, \"S2\", 56)","checkIn(47, \"S1\", 47)","checkOut(47, \"S2\", 57)","checkIn(48, \"S1\", 48)","checkOut(48, \"S2\", 58)","checkIn(49, \"S1\", 49)","checkOut(49, \"S2\", 59)","checkIn(50, \"S1\", 50)","checkOut(50, \"S2\", 60)","checkIn(51, \"S1\", 51)","checkOut(51, \"S2\", 61)","checkIn(52, \"S1\", 52)","checkOut(52, \"S2\", 62)","checkIn(53, \"S1\", 53)","checkOut(53, \"S2\", 63)","checkIn(54, \"S1\", 54)","checkOut(54, \"S2\", 64)","checkIn(55, \"S1\", 55)","checkOut(55, \"S2\", 65)","checkIn(56, \"S1\", 56)","checkOut(56, \"S2\", 66)","checkIn(57, \"S1\", 57)","checkOut(57, \"S2\", 67)","checkIn(58, \"S1\", 58)","checkOut(58, \"S2\", 68)","checkIn(59, \"S1\", 59)","checkOut(59, \"S2\", 69)","checkIn(60, \"S1\", 60)","checkOut(60, \"S2\", 70)","checkIn(61, \"S1\", 61)","checkOut(61, \"S2\", 71)","checkIn(62, \"S1\", 62)","checkOut(62, \"S2\", 72)","checkIn(63, \"S1\", 63)","checkOut(63, \"S2\", 73)","checkIn(64, \"S1\", 64)","checkOut(64, \"S2\", 74)","checkIn(65, \"S1\", 65)","checkOut(65, \"S2\", 75)","checkIn(66, \"S1\", 66)","checkOut(66, \"S2\", 76)","checkIn(67, \"S1\", 67)","checkOut(67, \"S2\", 77)","checkIn(68, \"S1\", 68)","checkOut(68, \"S2\", 78)","checkIn(69, \"S1\", 69)","checkOut(69, \"S2\", 79)","checkIn(70, \"S1\", 70)","checkOut(70, \"S2\", 80)","checkIn(71, \"S1\", 71)","checkOut(71, \"S2\", 81)","checkIn(72, \"S1\", 72)","checkOut(72, \"S2\", 82)","checkIn(73, \"S1\", 73)","checkOut(73, \"S2\", 83)","checkIn(74, \"S1\", 74)","checkOut(74, \"S2\", 84)","checkIn(75, \"S1\", 75)","checkOut(75, \"S2\", 85)","checkIn(76, \"S1\", 76)","checkOut(76, \"S2\", 86)","checkIn(77, \"S1\", 77)","checkOut(77, \"S2\", 87)","checkIn(78, \"S1\", 78)","checkOut(78, \"S2\", 88)","checkIn(79, \"S1\", 79)","checkOut(79, \"S2\", 89)","checkIn(80, \"S1\", 80)","checkOut(80, \"S2\", 90)","checkIn(81, \"S1\", 81)","checkOut(81, \"S2\", 91)","checkIn(82, \"S1\", 82)","checkOut(82, \"S2\", 92)","checkIn(83, \"S1\", 83)","checkOut(83, \"S2\", 93)","checkIn(84, \"S1\", 84)","checkOut(84, \"S2\", 94)","checkIn(85, \"S1\", 85)","checkOut(85, \"S2\", 95)","checkIn(86, \"S1\", 86)","checkOut(86, \"S2\", 96)","checkIn(87, \"S1\", 87)","checkOut(87, \"S2\", 97)","checkIn(88, \"S1\", 88)","checkOut(88, \"S2\", 98)","checkIn(89, \"S1\", 89)","checkOut(89, \"S2\", 99)","checkIn(90, \"S1\", 90)","checkOut(90, \"S2\", 100)","checkIn(91, \"S1\", 91)","checkOut(91, \"S2\", 101)","checkIn(92, \"S1\", 92)","checkOut(92, \"S2\", 102)","checkIn(93, \"S1\", 93)","checkOut(93, \"S2\", 103)","checkIn(94, \"S1\", 94)","checkOut(94, \"S2\", 104)","checkIn(95, \"S1\", 95)","checkOut(95, \"S2\", 105)","checkIn(96, \"S1\", 96)","checkOut(96, \"S2\", 106)","checkIn(97, \"S1\", 97)","checkOut(97, \"S2\", 107)","checkIn(98, \"S1\", 98)","checkOut(98, \"S2\", 108)","checkIn(99, \"S1\", 99)","checkOut(99, \"S2\", 109)","checkIn(100, \"S1\", 100)","checkOut(100, \"S2\", 110)","getAverageTime(\"S1\", \"S2\")","getAverageTime(\"S1\", \"S2\")","getAverageTime(\"S1\", \"S2\")","getAverageTime(\"S1\", \"S2\")","getAverageTime(\"S1\", \"S2\")","getAverageTime(\"S1\", \"S2\")","getAverageTime(\"S1\", \"S2\")","getAverageTime(\"S1\", \"S2\")","getAverageTime(\"S1\", \"S2\")","getAverageTime(\"S1\", \"S2\")"]
⏱ Performance - must finish in 2000ms

Large input with 100 check-in/out pairs and 10 average queries to test O(1) per operation efficiency.

Practice

(1/5)
1. You are given a singly linked list where each node contains an additional random pointer that can point to any node in the list or null. The task is to create a deep copy of this list, preserving both next and random pointers. Which approach guarantees an optimal solution with O(n) time and O(1) extra space?
easy
A. Sort nodes by their values and then copy them sequentially, assigning random pointers based on sorted order.
B. Use a brute force approach with nested loops to assign random pointers after copying nodes.
C. Use dynamic programming to store intermediate results of copied nodes and their random pointers.
D. Interleave copied nodes within the original list, assign random pointers using the interleaved structure, then separate the lists.

Solution

  1. Step 1: Understand the problem constraints

    The problem requires copying a linked list with random pointers efficiently, preserving structure.
  2. Step 2: Identify the optimal approach

    Interleaving copied nodes within the original list allows O(1) extra space and O(n) time by leveraging the original list's structure to assign random pointers without extra data structures.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Interleaving nodes avoids extra hash maps and nested loops [OK]
Hint: Interleaving nodes enables O(1) space copying [OK]
Common Mistakes:
  • Assuming nested loops are needed for random pointer assignment
  • Thinking sorting helps with random pointers
  • Confusing DP with linked list copying
2. Identify the bug in the following LRU Cache implementation snippet that causes incorrect eviction behavior when capacity is exceeded:
def put(self, key: int, value: int) -> None:
    if key in self.cache:
        self._remove(self.cache[key])
    node = Node(key, value)
    self._add(node)
    self.cache[key] = node
    if len(self.cache) > self.capacity:
        lru = self.head.next  # Bug here
        self._remove(lru)
        del self.cache[lru.key]
medium
A. Evicting the node at head.next instead of tail.prev
B. Removing node from cache before adding new node
C. Not updating the cache dictionary after eviction
D. Not checking if capacity is zero before eviction

Solution

  1. Step 1: Understand eviction logic

    Eviction must remove the least recently used node, which is at tail.prev, not head.next.
  2. Step 2: Identify bug

    Code incorrectly evicts head.next, which is the most recently used node, causing wrong eviction.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Evicting head.next breaks LRU policy, must evict tail.prev [OK]
Hint: LRU node is at tail.prev, not head.next [OK]
Common Mistakes:
  • Evicting MRU instead of LRU
  • Forgetting to delete from cache dict
  • Not handling zero capacity
3. Suppose you want to modify the skiplist to support duplicate values and allow searching for the k-th occurrence of a target. Which modification is the most appropriate to maintain efficient search and insertion?
hard
A. Insert duplicate nodes at the lowest level only, ignoring higher levels
B. Store a count of duplicates in each node and update counts on insert/erase
C. Use a hash map to track occurrences separately from the skiplist
D. Disallow duplicates and reject insertions of existing values

Solution

  1. Step 1: Understand duplicate handling

    To support duplicates and k-th occurrence search, nodes must track counts or occurrences.
  2. Step 2: Evaluate options

    Storing counts in nodes and updating them maintains skiplist structure and efficient operations.
  3. Step 3: Reject other options

    Inserting duplicates only at lowest level breaks skiplist invariants; hash map adds overhead; disallowing duplicates contradicts requirement.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Counting duplicates in nodes preserves skiplist efficiency and supports k-th occurrence [OK]
Hint: Count duplicates in nodes to handle multiple occurrences efficiently [OK]
Common Mistakes:
  • Ignoring duplicates in higher levels
  • Using external structures breaking skiplist properties
  • Disallowing duplicates despite requirement
4. Suppose the Twitter design is extended so that users can post the same tweet multiple times (tweet reuse allowed). Which modification to the optimal approach is necessary to correctly handle this scenario?
hard
A. Allow multiple TweetNode instances with the same tweetId but different timestamps in the linked list and heap.
B. Use a hash set to track unique tweetIds per user to avoid duplicates in the feed.
C. Modify the heap to store tweetIds only once, ignoring repeated posts of the same tweetId.
D. Disallow posting duplicate tweetIds by rejecting posts with existing tweetIds.

Solution

  1. Step 1: Understand tweet reuse impact

    Allowing duplicate tweetIds means multiple posts with same id but different times must be treated as distinct tweets.
  2. Step 2: Adjust data structures

    Each post creates a new TweetNode with unique timestamp; heap and linked lists handle duplicates naturally by time ordering.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Multiple nodes with same tweetId but different times must be stored separately [OK]
Hint: Treat each post as unique by timestamp, even if tweetId repeats [OK]
Common Mistakes:
  • Trying to deduplicate tweets in feed incorrectly
  • Rejecting duplicate posts unnecessarily
5. Suppose the data structure must now support a new operation: getRandomWeighted(), which returns a random element with probability proportional to the number of times it appears (its frequency). Which modification to the optimized Insert Delete GetRandom O(1) with duplicates data structure is correct to support this efficiently?
hard
A. Maintain a list with each element repeated as many times as its frequency; getRandomWeighted() picks uniformly from this list.
B. Use a hash map from element to frequency and sample elements proportionally using a prefix sum array updated on insert/remove.
C. Use the existing list and hash map without changes; getRandomWeighted() is same as getRandom().
D. Store frequencies in a balanced BST and perform weighted random selection by traversing the tree.

Solution

  1. Step 1: Understand weighted random requirements

    getRandomWeighted() must return elements with probability proportional to their frequency.
  2. Step 2: Evaluate options

    Maintain a list with each element repeated as many times as its frequency; getRandomWeighted() picks uniformly from this list. duplicates elements in list, causing O(n) space and slow updates. Use the existing list and hash map without changes; getRandomWeighted() is same as getRandom(). ignores weighting. Store frequencies in a balanced BST and perform weighted random selection by traversing the tree. adds complexity and O(log n) operations. Use a hash map from element to frequency and sample elements proportionally using a prefix sum array updated on insert/remove. maintains frequencies and prefix sums, enabling O(log n) or better weighted sampling with efficient updates.
  3. Step 3: Choose best tradeoff

    Use a hash map from element to frequency and sample elements proportionally using a prefix sum array updated on insert/remove. is the most efficient and scalable approach to support weighted random sampling with dynamic updates.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Prefix sums enable efficient weighted sampling [OK]
Hint: Prefix sums on frequencies enable weighted random sampling [OK]
Common Mistakes:
  • Using naive repeated list causing high space
  • Ignoring frequency updates
  • Assuming getRandom() suffices for weighted sampling
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