This is part of a series of Leetcode solution explanations (index). If you liked this solution or found it useful, please like this post and/or upvote my solution post on Leetcode's forums.
Leetcode Problem #1396 (Medium): Design Underground System
Description:
(Jump to: Solution Idea || Code: JavaScript | Python | Java | C++)
Implement the UndergroundSystem class:
void checkIn(int id, string stationName, int t)
- A customer with a card id equal to
id
, gets in the stationstationName
at timet
.- A customer can only be checked into one place at a time.
void checkOut(int id, string stationName, int t)
- A customer with a card id equal to
id
, gets out from the stationstationName
at timet
.double getAverageTime(string startStation, string endStation)
- Returns the average time to travel between the
startStation
and theendStation
.- The average time is computed from all the previous traveling from
startStation
toendStation
that happened directly.- Call to
getAverageTime
is always valid.You can assume all calls to
checkIn
andcheckOut
methods are consistent. If a customer gets in at timet1
at some station, they get out at timet2
witht2 > t1
. All events happen in chronological order.
Examples:
Example 1: | |
---|---|
Input: | example |
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);
undergroundSystem.checkOut(27, "Waterloo", 20);
undergroundSystem.checkOut(32, "Cambridge", 22);
undergroundSystem.getAverageTime("Paradise", "Cambridge"); // return 14.00000. There was only one travel from "Paradise" (at time 8) to "Cambridge" (at time 22)
undergroundSystem.getAverageTime("Leyton", "Waterloo"); // return 11.00000. There were two travels from "Leyton" to "Waterloo", a customer with id=45 from time=3 to time=15 and a customer with id=27 from time=10 to time=20. So the average time is ( (15-3) + (20-10) ) / 2 = 11.00000
undergroundSystem.checkIn(10, "Leyton", 24);
undergroundSystem.getAverageTime("Leyton", "Waterloo"); // return 11.00000
undergroundSystem.checkOut(10, "Waterloo", 38);
undergroundSystem.getAverageTime("Leyton", "Waterloo"); // return 12.00000
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);
undergroundSystem.getAverageTime("Leyton", "Paradise"); // return 5.00000
undergroundSystem.checkIn(5, "Leyton", 10);
undergroundSystem.checkOut(5, "Paradise", 16);
undergroundSystem.getAverageTime("Leyton", "Paradise"); // return 5.50000
undergroundSystem.checkIn(2, "Leyton", 21);
undergroundSystem.checkOut(2, "Paradise", 30);
undergroundSystem.getAverageTime("Leyton", "Paradise"); // return 6.66667
Constraints:
- There will be at most
20000
operations.1 <= id, t <= 10^6
- All strings consist of uppercase and lowercase English letters, and digits.
1 <= stationName.length <= 10
- Answers within
10^-5
of the actual value will be accepted as correct.
Idea:
(Jump to: Problem Description || Code: JavaScript | Python | Java | C++)
The first thing we should realize is that since checkins and checkouts are separated, we'll need some type of data structure in which to store checkin information until we find the matching checkout information.
The next important realization is that since we only ultimately care about route times, we don't really need to store checkout info at all. As long as we store the checkin info until we get to the checkout info, we can actually just then store the trip info separately by route and get rid of the checkin and checkout information. This will help keep the space needed to a minimum.
As we'll want to look up checkin and route info by id and route name, we should use Map structures for both (checkins & routes). For the route information, we'll only need to keep track of the number of trips and the total duration, so that we can calculate the average as needed. We can also use a concatenated name for the key in the route map in order to store the trip as a whole, rather than having to keep track of both ends separately.
Implementation:
For better efficiency, Javascript can use a Uint32Array for route, Python can use a tuple for checkins, and Java and C++ can use Pairs for checkins and Java can use Pairs instead of concatenating the station names.
Javascript Code:
(Jump to: Problem Description || Solution Idea)
class UndergroundSystem {
constructor() {
this.checkins = new Map()
this.routes = new Map()
}
checkIn(id, stationName, t) {
this.checkins.set(id, [stationName, t])
};
checkOut(id, stationName, t) {
let [stn, start] = this.checkins.get(id),
route = stn + "," + stationName
this.checkins.delete(id)
if (!this.routes.has(route))
this.routes.set(route, new Uint32Array(2))
let trip = this.routes.get(route)
trip[0]++, trip[1] += t - start
};
getAverageTime(startStation, endStation) {
let [count, sum] = this.routes.get(startStation + "," + endStation)
return sum / count
};
};
Python Code:
(Jump to: Problem Description || Solution Idea)
class UndergroundSystem:
def __init__(self):
self.checkins = defaultdict()
self.routes = defaultdict()
def checkIn(self, id: int, stationName: str, t: int) -> None:
self.checkins[id] = (stationName, t)
def checkOut(self, id: int, stationName: str, t: int) -> None:
stn, start = self.checkins[id]
del self.checkins[id]
route = stn + "," + stationName
if route not in self.routes: self.routes[route] = [0,0]
trip = self.routes[route]
trip[0] += 1
trip[1] += t - start
def getAverageTime(self, startStation: str, endStation: str) -> float:
count, rsum = self.routes[startStation + "," + endStation]
return rsum / count
Java Code:
(Jump to: Problem Description || Solution Idea)
class UndergroundSystem {
Map<Integer, Pair<String, Integer>> checkins = new HashMap<>();
Map<Pair<String, String>, int[]> routes = new HashMap<>();
public void checkIn(int id, String stationName, int t) {
checkins.put(id, new Pair(stationName, t));
}
public void checkOut(int id, String stationName, int t) {
Pair<String, Integer> cIn = checkins.get(id);
checkins.remove(id);
Pair<String, String> route = new Pair(cIn.getKey(), stationName);
int[] trip = routes.getOrDefault(route, new int[2]);
trip[0]++;
trip[1] += t - cIn.getValue();
routes.put(route, trip);
}
public double getAverageTime(String startStation, String endStation) {
int[] trip = routes.get(new Pair(startStation, endStation));
return (double)trip[1] / trip[0];
}
}
C++ Code:
(Jump to: Problem Description || Solution Idea)
class UndergroundSystem {
public:
unordered_map<int, pair<string, int>> checkins;
unordered_map<string, pair<int, int>> routes;
void checkIn(int id, string stationName, int t) {
checkins[id] = {stationName, t};
}
void checkOut(int id, string stationName, int t) {
auto [stn, start] = checkins[id];
checkins.erase(id);
string route = stn + "," + stationName;
routes[route].first++, routes[route].second += t - start;
}
double getAverageTime(string startStation, string endStation) {
auto& [count, sum] = routes[startStation + "," + endStation];
return (double)sum / count;
}
};
Top comments (0)