Bird
Raised Fist0
Interview Prepcustom-data-structuresmediumAmazonGoogleFacebook

Design Underground System

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
</>
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 divisionReturn 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 orderCompute 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 correctlyUse 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 properlyUse (startStation, endStation) as key for totals and counts
Wrong: TLE on large inputsUsing O(n) scan of trips on getAverageTime instead of running totalsMaintain 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 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
2. What is the worst-case time complexity of the in-place iterative flattening algorithm for a multilevel doubly linked list where each node may have a child list, and why?
medium
A. O(n) because each node is visited once.
B. O(n) with O(n) auxiliary space for recursion stack.
C. O(n log n) due to repeated merging of child lists.
D. O(n^2) because finding the tail of each child list requires traversing child nodes repeatedly.

Solution

  1. Step 1: Identify the costly operation

    Finding the tail of each child list requires traversing that child list fully.
  2. Step 2: Analyze nested traversal effect

    Since this tail-finding happens for multiple nodes, total time can be quadratic in number of nodes.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Repeated tail traversal causes O(n^2) time in worst case [OK]
Hint: Repeated tail search causes quadratic time [OK]
Common Mistakes:
  • Assuming linear time because each node is visited once
  • Confusing recursion stack space with iterative approach
3. Consider the following buggy implementation of SnapshotArray. Which line contains the subtle bug that can cause incorrect get results when multiple sets occur before a snap on the same index?
import bisect

class SnapshotArray:
    def __init__(self, length: int):
        self.snap_id = 0
        self.data = [[(-1, 0)] for _ in range(length)]

    def set(self, index: int, val: int) -> None:
        # Bug here
        self.data[index].append((self.snap_id, val))

    def snap(self) -> int:
        self.snap_id += 1
        return self.snap_id - 1

    def get(self, index: int, snap_id: int) -> int:
        arr = self.data[index]
        i = bisect.bisect_right(arr, (snap_id, float('inf'))) - 1
        return arr[i][1]
medium
A. Line in snap(): incrementing snap_id before returning
B. Line in __init__(): initializing with (-1, 0) instead of (0, 0)
C. Line in set(): always appending without checking if last snap_id matches
D. Line in get(): using bisect_right instead of bisect_left

Solution

  1. Step 1: Analyze set() method

    The set method always appends (snap_id, val) without checking if the last entry has the same snap_id, causing duplicate entries for the same snap_id.
  2. Step 2: Consequence on get()

    Duplicate entries for the same snap_id break binary search assumptions and can cause get to return incorrect values.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Correct code replaces last entry if snap_id matches [OK]
Hint: Check if last snap_id matches before appending in set() [OK]
Common Mistakes:
  • Appending blindly causes duplicates
  • Misunderstanding initialization defaults
4. 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
5. Suppose the multilevel doubly linked list can have cycles introduced via child pointers (i.e., a child pointer may point to an ancestor node). Which modification to the flattening algorithm is necessary to handle this safely?
hard
A. Use recursion with a depth limit to prevent stack overflow on cycles.
B. Remove all child pointers before flattening to guarantee acyclic structure.
C. Use a hash set to track visited nodes and skip already visited ones to avoid infinite loops.
D. No modification needed; the existing in-place iterative approach handles cycles naturally.

Solution

  1. Step 1: Understand the problem with cycles

    Cycles cause infinite loops during traversal if nodes are revisited.
  2. Step 2: Identify safe traversal method

    Tracking visited nodes with a hash set prevents revisiting and infinite loops.
  3. Step 3: Evaluate other options

    Removing child pointers loses structure; recursion depth limit is unreliable; existing code does not detect cycles.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    Visited set prevents infinite loops in cyclic graphs [OK]
Hint: Track visited nodes to handle cycles safely [OK]
Common Mistakes:
  • Assuming no cycles exist in input
  • Relying on recursion depth limits
  • Ignoring cycle detection altogether