The Runtime Theory
mediumleetcode#low-level-design#object-oriented

Design Underground System

Track check-in/check-out travel events for an underground transit system and answer average travel-time queries between any two stations in near-constant time.

The Runtime Theory Team2 min read
Solve it

solving happens on the judge — come back and mark it done

Sample cases

incheckIn(45,'Leyton',3); checkOut(45,'Waterloo',15)

outgetAverageTime('Leyton','Waterloo') = 12.0

incheckIn(27,'Leyton',10); checkOut(27,'Waterloo',20)

outgetAverageTime('Leyton','Waterloo') = 15.0

intwo riders, same route, times 12 and 10

outaverage = 11.0

incheckOut before any checkIn for that id

outKeyError or undefined behavior — caller contract prevents it

The machine must record two kinds of events and answer one question. A rider checks in at a station with a timestamp; later they check out at another station with another timestamp. Given a station pair, the machine must return the mean of all completed trips between them, including trips still in flight.

Two maps do the work. The first map holds active riders: rider id → (station, check-in time). When checkIn fires, the machine writes the entry, overwriting any prior entry for that id (a rider is never double-checked-in). When checkOut fires, the machine pops the active entry, computes elapsed = checkout time − check-in time, and folds it into the second map: (startStation, endStation) → [totalTime, tripCount]. The pair is keyed by a tuple or a flattened string like "Leyton,Waterloo" — string concatenation with a delimiter beats nested dicts for speed and clarity.

getAverageTime reads one entry from the second map and returns totalTime / tripCount. Everything is O(1) per operation; no scanning ever happens. The only arithmetic subtlety is floating point: the machine returns a float from integer division, so the average must be computed as totalTime / tripCount in Python 3 semantics, which yields a float automatically.

Edge cases: a rider checking out with no active check-in is a caller error the machine does not guard against; the spec guarantees valid interleavings. Multiple riders on the same route accumulate into the same bucket — order of check-out does not matter because the pair, not the rider, keys the bucket.

python
class UndergroundSystem:
    def __init__(self):
        self.active = {}                      # id -> (station, t)
        self.trips = {}                       # (start, end) -> [total, count]
 
    def checkIn(self, id, stationName, t):
        self.active[id] = (stationName, t)
 
    def checkOut(self, id, stationName, t):
        start, t0 = self.active.pop(id)
        key = (start, stationName)
        total, count = self.trips.get(key, (0, 0))
        self.trips[key] = (total + t - t0, count + 1)
 
    def getAverageTime(self, startStation, endStation):
        total, count = self.trips[(startStation, endStation)]
        return total / count

More practice in this topic

One dispatch a week

The trace behind each problem, the tradeoff that explains it, and one technical dispatch per week — no noise.

One technical dispatch per week. No noise.