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 <= 106
1 <= stationName.length, startStation.length, endStation.length <= 10
2 * 104
calls in total to checkIn
, checkOut
, and getAverageTime
.10-5
of the actual value will be accepted.This problem requires designing a system to track customer travel times on an underground railway. The system needs to handle check-ins, check-outs, and calculating average travel times between stations. The optimal solution uses hash tables (or dictionaries) for efficient data storage and retrieval.
Data Structures:
The core of the solution lies in using two hash tables:
checkin_data
(or ts
in the code): This hash table stores information about currently checked-in passengers. The key is the passenger's ID (id
), and the value is a tuple (or similar structure) containing the check-in time (t
) and the check-in station (stationName
).
travel_times
(or d
in the code): This hash table stores the accumulated travel times between station pairs. The key is a tuple representing the start and end stations ((startStation, endStation)
), and the value is another tuple containing the total travel time for that pair and the number of trips made between those stations.
Methods:
checkIn(id, stationName, t)
: This method simply adds the passenger's ID, check-in station, and time to the checkin_data
hash table. The lookup and insertion in a hash table takes O(1) on average.
checkOut(id, stationName, t)
: This method does the following:
checkin_data
using the passenger ID. O(1) lookup.t - t_checkin
).travel_times
hash table. If the (startStation, endStation)
pair already exists, it adds the travel time to the total and increments the trip count; otherwise, it creates a new entry. This is an O(1) operation on average.getAverageTime(startStation, endStation)
: This method retrieves the total travel time and trip count from travel_times
for the given station pair. It then calculates and returns the average travel time. The lookup in the hash table takes O(1) on average.
Time and Space Complexity:
Time Complexity: All three methods (checkIn
, checkOut
, getAverageTime
) have an average time complexity of O(1) because they primarily involve hash table lookups and updates, which are typically constant-time operations. In the worst case (hash collisions), it could become O(n), where n is the number of entries in the hash table, but this is unlikely with a good hash function.
Space Complexity: The space complexity is O(n+m), where 'n' is the number of unique passenger IDs and 'm' is the number of unique station pairs. This is because the size of the hash tables grows linearly with the number of passengers and the number of distinct station pairs.
Code Examples (Python):
class UndergroundSystem:
def __init__(self):
self.checkin_data = {}
self.travel_times = {}
def checkIn(self, id: int, stationName: str, t: int) -> None:
self.checkin_data[id] = (t, stationName)
def checkOut(self, id: int, stationName: str, t: int) -> None:
t_checkin, start_station = self.checkin_data[id]
travel_time = t - t_checkin
key = (start_station, stationName)
if key in self.travel_times:
total_time, count = self.travel_times[key]
self.travel_times[key] = (total_time + travel_time, count + 1)
else:
self.travel_times[key] = (travel_time, 1)
def getAverageTime(self, startStation: str, endStation: str) -> float:
total_time, count = self.travel_times[(startStation, endStation)]
return total_time / count
The provided Java, C++, and Go code examples follow the same logic and data structures, achieving the same time and space complexity. They differ only in syntax and specific data structure implementations.