An underground railway system is keeping track of customer travel times between different stations. They are using this data to calculate the average time it takes to travel from one station to another.
Implement the UndergroundSystem class:
void checkIn(int id, string stationName, int t)
id, checks in at the station stationName at time t.void checkOut(int id, string stationName, int t)
id, checks out from the station stationName at time t.double getAverageTime(string startStation, string endStation)
startStation to endStation.startStation to endStation that happened directly, meaning a check in at startStation followed by a check out from endStation.startStation to endStation may be different from the time it takes to travel from endStation to startStation.startStation to endStation before getAverageTime is called.You may assume all calls to the checkIn and checkOut methods are consistent. If a customer checks in at time t1 then checks out at time t2, then t1 < t2. All events happen in chronological order.
Example 1:
Input
["UndergroundSystem","checkIn","checkIn","checkIn","checkOut","checkOut","checkOut","getAverageTime","getAverageTime","checkIn","getAverageTime","checkOut","getAverageTime"]
[[],[45,"Leyton",3],[32,"Paradise",8],[27,"Leyton",10],[45,"Waterloo",15],[27,"Waterloo",20],[32,"Cambridge",22],["Paradise","Cambridge"],["Leyton","Waterloo"],[10,"Leyton",24],["Leyton","Waterloo"],[10,"Waterloo",38],["Leyton","Waterloo"]]
Output
[null,null,null,null,null,null,null,14.00000,11.00000,null,11.00000,null,12.00000]
Explanation
UndergroundSystem undergroundSystem = new UndergroundSystem();
undergroundSystem.checkIn(45, "Leyton", 3);
undergroundSystem.checkIn(32, "Paradise", 8);
undergroundSystem.checkIn(27, "Leyton", 10);
undergroundSystem.checkOut(45, "Waterloo", 15); // Customer 45 "Leyton" -> "Waterloo" in 15-3 = 12
undergroundSystem.checkOut(27, "Waterloo", 20); // Customer 27 "Leyton" -> "Waterloo" in 20-10 = 10
undergroundSystem.checkOut(32, "Cambridge", 22); // Customer 32 "Paradise" -> "Cambridge" in 22-8 = 14
undergroundSystem.getAverageTime("Paradise", "Cambridge"); // return 14.00000. One trip "Paradise" -> "Cambridge", (14) / 1 = 14
undergroundSystem.getAverageTime("Leyton", "Waterloo"); // return 11.00000. Two trips "Leyton" -> "Waterloo", (10 + 12) / 2 = 11
undergroundSystem.checkIn(10, "Leyton", 24);
undergroundSystem.getAverageTime("Leyton", "Waterloo"); // return 11.00000
undergroundSystem.checkOut(10, "Waterloo", 38); // Customer 10 "Leyton" -> "Waterloo" in 38-24 = 14
undergroundSystem.getAverageTime("Leyton", "Waterloo"); // return 12.00000. Three trips "Leyton" -> "Waterloo", (10 + 12 + 14) / 3 = 12
Example 2:
Input
["UndergroundSystem","checkIn","checkOut","getAverageTime","checkIn","checkOut","getAverageTime","checkIn","checkOut","getAverageTime"]
[[],[10,"Leyton",3],[10,"Paradise",8],["Leyton","Paradise"],[5,"Leyton",10],[5,"Paradise",16],["Leyton","Paradise"],[2,"Leyton",21],[2,"Paradise",30],["Leyton","Paradise"]]
Output
[null,null,null,5.00000,null,null,5.50000,null,null,6.66667]
Explanation
UndergroundSystem undergroundSystem = new UndergroundSystem();
undergroundSystem.checkIn(10, "Leyton", 3);
undergroundSystem.checkOut(10, "Paradise", 8); // Customer 10 "Leyton" -> "Paradise" in 8-3 = 5
undergroundSystem.getAverageTime("Leyton", "Paradise"); // return 5.00000, (5) / 1 = 5
undergroundSystem.checkIn(5, "Leyton", 10);
undergroundSystem.checkOut(5, "Paradise", 16); // Customer 5 "Leyton" -> "Paradise" in 16-10 = 6
undergroundSystem.getAverageTime("Leyton", "Paradise"); // return 5.50000, (5 + 6) / 2 = 5.5
undergroundSystem.checkIn(2, "Leyton", 21);
undergroundSystem.checkOut(2, "Paradise", 30); // Customer 2 "Leyton" -> "Paradise" in 30-21 = 9
undergroundSystem.getAverageTime("Leyton", "Paradise"); // return 6.66667, (5 + 6 + 9) / 3 = 6.66667
Constraints:
1 <= id, t <= 1061 <= stationName.length, startStation.length, endStation.length <= 102 * 104 calls in total to checkIn, checkOut, and getAverageTime.10-5 of the actual value will be accepted.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for the underground system problem means we record every single trip detail as it happens. When asked for the average time between two stations, we go through all recorded trips to find the ones that match those stations.
Here's how the algorithm would work step-by-step:
class UndergroundSystem:
def __init__(self):
self.trip_history = []
def checkIn(self, passenger_id: int, station_name: str, travel_time: int) -> None:
self.trip_history.append({
'passenger_id': passenger_id,
'start_station': station_name,
'start_time': travel_time,
'end_station': None,
'end_time': None
})
def checkOut(self, passenger_id: int, station_name: str, travel_time: int) -> None:
for trip in self.trip_history:
if trip['passenger_id'] == passenger_id and trip['end_station'] is None:
trip['end_station'] = station_name
trip['end_time'] = travel_time
break
def getAverageTime(self, start_station: str, end_station: str) -> float:
total_travel_time = 0
number_of_trips = 0
# Iterate over all trips to find the relevant ones
for trip in self.trip_history:
if trip['start_station'] == start_station and trip['end_station'] == end_station:
#Calculate time for each trip and increment totals
total_travel_time += trip['end_time'] - trip['start_time']
number_of_trips += 1
# Prevent division by zero
if number_of_trips == 0:
return 0.0
# Average time calculation
average_time = total_travel_time / number_of_trips
return average_timeThe underground system problem is best solved by remembering key information as people travel. We want to efficiently store and retrieve average travel times between stations. To do this, we'll use memory to track who is travelling and calculations to determine average journey times.
Here's how the algorithm would work step-by-step:
class UndergroundSystem:
def __init__(self):
self.passenger_check_in = {}
self.station_travel_times = {}
def checkIn(self, passenger_id: int, start_station: str, travel_time: int) -> None:
self.passenger_check_in[passenger_id] = (start_station, travel_time)
def checkOut(self, passenger_id: int, end_station: str, travel_time: int) -> None:
start_station, check_in_time = self.passenger_check_in[passenger_id]
del self.passenger_check_in[passenger_id]
journey_route = (start_station, end_station)
travel_duration = travel_time - check_in_time
# Store total time and count for calculating average
if journey_route in self.station_travel_times:
total_time, journey_count = self.station_travel_times[journey_route]
self.station_travel_times[journey_route] = (total_time + travel_duration, journey_count + 1)
else:
self.station_travel_times[journey_route] = (travel_duration, 1)
def getAverageTime(self, start_station: str, end_station: str) -> float:
journey_route = (start_station, end_station)
# Retrieve the precalculated values
total_travel_time, journey_count = self.station_travel_times[journey_route]
# Return average
return total_travel_time / journey_count| Case | How to Handle |
|---|---|
| Multiple people at the same station and time | The enterStation method should handle multiple concurrent check-ins at the same station and time by using customer ID as the unique identifier. |
| Same person checking in at the same station multiple times without checking out | Overwrite previous check-in data for the customer ID to only consider the latest check-in. |
| getAverageTime called before any customers complete a journey between two stations | Return 0 or throw an exception indicating no journeys have been completed yet. |
| Integer overflow in calculating total time or count | Use long data type for storing total time and count to avoid integer overflow. |
| Station names are the same for start and end stations | The average time should still be calculated and stored separately for journeys from station A to station A if such journeys exist. |
| Large number of concurrent customers using the system | Use efficient data structures like HashMaps for fast lookups and consider memory usage for a large number of customers. |
| Extremely large time values | Using long type should mitigate most cases, but be aware of the limits of long and consider alternative representations if needed for extremely large time values. |
| Customer forgets to check out | There is no graceful recovery, this incomplete data pollutes the average and should be discarded or handled offline, as online recovery might be inconsistent. |