1396-设计地铁系统

Raphael Liu Lv10

地铁系统跟踪不同车站之间的乘客出行时间,并使用这一数据来计算从一站到另一站的平均时间。

实现 UndergroundSystem 类:

  • void checkIn(int id, string stationName, int t)
    • 通行卡 ID 等于 id 的乘客,在时间 t ,从 stationName 站进入
    • 乘客一次只能从一个站进入
  • void checkOut(int id, string stationName, int t)
    • 通行卡 ID 等于 id 的乘客,在时间 t ,从 stationName 站离开
  • double getAverageTime(string startStation, string endStation)
    • 返回从 startStation 站到 endStation 站的平均时间
    • 平均时间会根据截至目前所有从 startStation直接 到达 endStation 站的行程进行计算,也就是从 startStation 站进入并从 endStation 离开的行程
    • startStationendStation 的行程时间与从 endStationstartStation 的行程时间可能不同
    • 在调用 getAverageTime 之前,至少有一名乘客从 startStation 站到达 endStation

你可以假设对 checkIncheckOut 方法的所有调用都是符合逻辑的。如果一名乘客在时间 t1 进站、时间 t2 出站,那么
t1 < t2 。所有时间都按时间顺序发生。

示例 1:

**输入**
["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"]]

**输出**
[null,null,null,null,null,null,null,14.00000,11.00000,null,11.00000,null,12.00000]

**解释**
UndergroundSystem undergroundSystem = new UndergroundSystem();
undergroundSystem.checkIn(45, "Leyton", 3);
undergroundSystem.checkIn(32, "Paradise", 8);
undergroundSystem.checkIn(27, "Leyton", 10);
undergroundSystem.checkOut(45, "Waterloo", 15);  // 乘客 45 "Leyton" -> "Waterloo" ,用时 15-3 = 12
undergroundSystem.checkOut(27, "Waterloo", 20);  // 乘客 27 "Leyton" -> "Waterloo" ,用时 20-10 = 10
undergroundSystem.checkOut(32, "Cambridge", 22); // 乘客 32 "Paradise" -> "Cambridge" ,用时 22-8 = 14
undergroundSystem.getAverageTime("Paradise", "Cambridge"); // 返回 14.00000 。只有一个 "Paradise" -> "Cambridge" 的行程,(14) / 1 = 14
undergroundSystem.getAverageTime("Leyton", "Waterloo");    // 返回 11.00000 。有两个 "Leyton" -> "Waterloo" 的行程,(10 + 12) / 2 = 11
undergroundSystem.checkIn(10, "Leyton", 24);
undergroundSystem.getAverageTime("Leyton", "Waterloo");    // 返回 11.00000
undergroundSystem.checkOut(10, "Waterloo", 38);  // 乘客 10 "Leyton" -> "Waterloo" ,用时 38-24 = 14
undergroundSystem.getAverageTime("Leyton", "Waterloo");    // 返回 12.00000 。有三个 "Leyton" -> "Waterloo" 的行程,(10 + 12 + 14) / 3 = 12

示例 2:

**输入**
["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"]]

**输出**
[null,null,null,5.00000,null,null,5.50000,null,null,6.66667]

**解释**
UndergroundSystem undergroundSystem = new UndergroundSystem();
undergroundSystem.checkIn(10, "Leyton", 3);
undergroundSystem.checkOut(10, "Paradise", 8); // 乘客 10 "Leyton" -> "Paradise" ,用时 8-3 = 5
undergroundSystem.getAverageTime("Leyton", "Paradise"); // 返回 5.00000 ,(5) / 1 = 5
undergroundSystem.checkIn(5, "Leyton", 10);
undergroundSystem.checkOut(5, "Paradise", 16); // 乘客 5 "Leyton" -> "Paradise" ,用时 16-10 = 6
undergroundSystem.getAverageTime("Leyton", "Paradise"); // 返回 5.50000 ,(5 + 6) / 2 = 5.5
undergroundSystem.checkIn(2, "Leyton", 21);
undergroundSystem.checkOut(2, "Paradise", 30); // 乘客 2 "Leyton" -> "Paradise" ,用时 30-21 = 9
undergroundSystem.getAverageTime("Leyton", "Paradise"); // 返回 6.66667 ,(5 + 6 + 9) / 3 = 6.66667

提示:

  • 1 <= id, t <= 106
  • 1 <= stationName.length, startStation.length, endStation.length <= 10
  • 所有字符串由大小写英文字母与数字组成
  • 总共最多调用 checkIncheckOutgetAverageTime 方法 2 * 104
  • 与标准答案误差在 10-5 以内的结果都被视为正确结果

方法一:哈希映射

我们需要两张哈希表。一张用来存编号为 id 的乘客的进站信息,键为 id,值需要保存两个信息:站点名字和进站时间。另一张用来存放以 s 为起点站,e 为终点站的已经出站的乘客的信息,键为 (s, e),值也需要保存两个信息:花费的总时间和已经出站的总人数。

checkIn 的时候,我们对第一张表进行操作,保存进站信息。在 checkOut 的时候,我们先从第一张表中查询这个 id 的进站信息 (startStation, startTime),然后修改第二张表,把总时间加上 t - startTime,总人数自增一。在 getAverageTime 的时候直接查询第二张表得到 (sum, amount),计算平均值。

代码

[sol1-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class UndergroundSystem {
public:
using Start = pair <string, int>;
using StartEnd = pair <string, string>;
using SumAndAmount = pair <int, int>;

struct StartEndHash {
int operator() (const StartEnd& x) const{
return hash <string> () (x.first + x.second);
}
};

unordered_map <int, Start> startInfo;
unordered_map <StartEnd, SumAndAmount, StartEndHash> table;

UndergroundSystem() {}

void checkIn(int id, string stationName, int t) {
startInfo[id] = {stationName, t};
}

void checkOut(int id, string stationName, int t) {
auto startTime = startInfo[id].second;
table[{startInfo[id].first, stationName}].first += t - startTime;
table[{startInfo[id].first, stationName}].second++;
}

double getAverageTime(string startStation, string endStation) {
auto sum = table[{startStation, endStation}].first;
auto amount = table[{startStation, endStation}].second;
return double(sum) / amount;
}
};
[sol1-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class UndergroundSystem {
class Start {
String station;
int time;

public Start(String station, int time) {
this.station = station;
this.time = time;
}
}

class StartEnd {
String start;
String end;

public StartEnd(String start, String end) {
this.start = start;
this.end = end;
}

public int hashCode() {
return (start + end).hashCode();
}

public boolean equals(Object obj2) {
if (obj2 instanceof StartEnd) {
StartEnd startEnd2 = (StartEnd) obj2;
return this.start.equals(startEnd2.start) && this.end.equals(startEnd2.end);
}
return false;
}
}

class SumAmount {
int sum;
int amount;

public SumAmount(int sum, int amount) {
this.sum = sum;
this.amount = amount;
}
}

Map<Integer, Start> startInfo;
Map<StartEnd, SumAmount> table;

public UndergroundSystem() {
startInfo = new HashMap<Integer, Start>();
table = new HashMap<StartEnd, SumAmount>();
}

public void checkIn(int id, String stationName, int t) {
startInfo.put(id, new Start(stationName, t));
}

public void checkOut(int id, String stationName, int t) {
Start start = startInfo.get(id);
String startStation = start.station;
int startTime = start.time;
StartEnd startEnd = new StartEnd(startStation, stationName);
SumAmount sumAmount = table.getOrDefault(startEnd, new SumAmount(0, 0));
sumAmount.sum += t - startTime;
sumAmount.amount++;
table.put(startEnd, sumAmount);
}

public double getAverageTime(String startStation, String endStation) {
StartEnd index = new StartEnd(startStation, endStation);
SumAmount sumAmount = table.get(index);
int sum = sumAmount.sum, amount = sumAmount.amount;
return 1.0 * sum / amount;
}
}
[sol1-Python]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class UndergroundSystem:
def __init__(self):
self.startInfo = dict()
self.table = dict()

def checkIn(self, id: int, stationName: str, t: int) -> None:
self.startInfo[id] = (stationName, t)

def checkOut(self, id: int, stationName: str, t: int) -> None:
startTime = self.startInfo[id][1]
index = (self.startInfo[id][0], stationName)
rec = self.table.get(index, (0, 0))
self.table[index] = (rec[0] + t - startTime, rec[1] + 1)


def getAverageTime(self, startStation: str, endStation: str) -> float:
index = (startStation, endStation)
sum, amount = self.table[index]
return sum / amount

复杂度分析

  • 时间复杂度:从代码可以看出,这里 checkIncheckOutgetAverageTime 的渐进时间复杂度都是 O(1)。

  • 空间复杂度:这里我们用到了两张哈希表作为辅助空间。假设这里操作的总次数为 n,那么第一张表的键的总数为 O(n),第二张表键的总数也为 O(n),故渐进空间复杂度为 O(n ^ 2)。

 Comments
On this page
1396-设计地铁系统