第二题 地铁闸机
某地铁站有一个闸机,同一时刻只允许一名乘客进站或出站,一个乘客通过闸机需要 1 秒钟。
如果在同一时刻,一个乘客要进站,另一个乘客要出站,按如下规则进出站:
l 如果上一秒,闸机没有被使用(即使更早被使用过),那么进站的乘客优先通过;
l 如果上一秒,闸机有乘客进站,那么进站的乘客优先;
l 如果上一秒,闸机有乘客出站,那么出站的乘客优先。
现有一群乘客要通过这个闸机,乘客的到达时刻记录于数组 arrTime 中(升序,可能有重复值),下标表示乘客的编号;乘客的进出站方向记录于direction中(0 表示出站,1 表示进站)。
请按乘客的编号顺序依次返回每个乘客实际通过闸机的时刻。
注意:若多人同时进站,按 arrTime 中下标从小到大顺序通过闸机;若多人同时出站,也按此处理。
Ø 示例 1:
输入:
arrTime = [0,0,1,5]
direction = [0,1,1,0]
输出:[2,0,1,5]
解释:
在 0 时刻,乘客 0(出站)和 1(进站)想要通过闸机。由于闸机上一秒没有被使用,所以乘客 1 优先进站,乘客 0 等待下一时刻;
在 1 时刻,乘客 2(进站)到达闸机,和乘客 0(出站)都要通过闸机。由于上一秒闸机有乘客进站,所以乘客 2 优先进站,乘客 0 继续等待下一时刻;
在 2 时刻,乘客 0 通过闸机;
在 5 时刻,乘客 3 通过闸机。
Ø 示例 2:
输入:
arrTime = [2,2,2,2,3,3,5,5,20,20]
direction = [0,1,1,0,0,1,1,0,0,1]
输出:[6,2,3,7,8,4,5,9,21,20]
解释:
在 2 时刻,乘客 0 和 3 同时出站,乘客 1 和 2 同时进站。由于闸机上一秒没有被使用,进站优先,且下标小的乘客 1 优先通过闸机;
在 20 时刻,乘客 8 要出站,乘客 9 要进站。由于闸机上一秒没有被使用(之前被使用过),所以乘客 9 优先通过闸机。
提示:
l 0 < arrTime.length == direction.length <= 10^5,arrTime[i] 和 direction[i] 分别表示第 i 个乘客的到达时刻和进出站方向
l 0 <= arrTime[i] <= arrTime[i+1] < 10^9
l direction[i] 仅为 0 或 1
答题要求:您编写的代码需要符合 CleanCode 的要求(包括通用编码规范、安全编码规范和圈复杂度)。
这道题看上去没有啥特别的算法,就是直译,条件判断稍麻烦,圈复杂度容易超出。
#include <vector>
#include <queue>
#include <gtest/gtest.h>
using namespace std;
class Solution {
public:
bool canIn(queue<pair<int, int>> &otherQueue, int machineState, int ticktime,
pair<int, int> &theOther) const {
bool shouldPass = false;
if (machineState == 0 || machineState == 1) {
shouldPass = true;
}
if (machineState == -1) {
if (otherQueue.size() > 0) {
theOther = otherQueue.front();
if (theOther.second > ticktime) {
shouldPass = true;
}
}
if (otherQueue.size() == 0) {
shouldPass = true;
}
}
return shouldPass;
}
bool canOut(queue<pair<int, int>> &inQueue, int machineState, int ticktime, const pair<int, int> &currOut,
pair<int, int> &currIn) const {
bool shouldPass = false;
if (currOut.second <= ticktime) {
if (machineState == 0 || machineState == -1) {
shouldPass = true;
}
if (machineState == 1) {
if (inQueue.size() > 0) {
currIn = inQueue.front();
if (currIn.second > ticktime) {
shouldPass = true;
}
}
if (inQueue.size() == 0) {
shouldPass = true;
}
}
}
return shouldPass;
}
vector<int> CalcInOutTime(vector<int> arrTimes, vector<int> directions) {
int size = arrTimes.size();
vector<int> result(size);
// 拆分为出站和进站两个队列
queue<pair<int, int>> inQueue;
queue<pair<int, int>> outQueue;
for (int i = 0; i < directions.size(); ++i) {
if (directions[i] == 1) {
inQueue.push({i, arrTimes[i]});
} else {
outQueue.push({i, arrTimes[i]});
}
}
// 以闸机视角处理
int machineState = 0; // 上一秒的状态,默认为空闲
int ticktime = arrTimes[0];
bool shouldPass = false; // 当前是否可以通过
pair<int, int> currIn;
pair<int, int> currOut;
while (ticktime < arrTimes[size - 1] + size) {
shouldPass = false;
// 进一人
if (inQueue.size() > 0) {
currIn = inQueue.front();
if (currIn.second <= ticktime) {
shouldPass = canIn(outQueue, machineState, ticktime, currOut);
}
if (shouldPass) {
result[currIn.first] = ticktime;
inQueue.pop();
machineState = 1; //进站
} else {
machineState = 0; // 空闲
}
}
// 出一人
if (!shouldPass && outQueue.size() > 0) {
currOut = outQueue.front();
shouldPass = canOut(inQueue, machineState, ticktime, currOut, currIn);
if (shouldPass) {
result[currOut.first] = ticktime;
outQueue.pop();
machineState = -1; // 出站
} else {
machineState = 0; // 空闲
}
}
// 不过人
ticktime++;
}
return result;
}
};
测试用例如下:
TEST(SubwayTestSuite, Test1) {
Solution s;
vector<int> arrTimes = {2, 2, 2, 2, 3, 3, 5, 5, 20, 20};
vector<int> directions = {0, 1, 1, 0, 0, 1, 1, 0, 0, 1};
vector<int> result = s.CalcInOutTime(arrTimes, directions);
vector<int> expected = {6, 2, 3, 7, 8, 4, 5, 9, 21, 20};
for (int i = 0; i < expected.size(); ++i) {
ASSERT_EQ(expected[i], result[i]);
}
}
TEST(SubwayTestSuite, Test2) {
Solution s;
vector<int> arrTimes = {0, 0, 1, 5};
vector<int> directions = {0, 1, 1, 0};
vector<int> result = s.CalcInOutTime(arrTimes, directions);
vector<int> expected = {2, 0, 1, 5};
for (int i = 0; i < expected.size(); ++i) {
ASSERT_EQ(expected[i], result[i]);
}
}