Hard
题目描述
在一条单车道上有 n 辆车沿着同一个方向行驶,这些车的速度各不相同。给你一个长度为 n 的数组 cars,其中 cars[i] = [positioni, speedi] 表示:
- positioni 是第 i 辆车与道路起点的距离(单位:米)。题目保证 positioni < positioni+1。
- speedi 是第 i 辆车的初始速度(单位:米/秒)。
为了简单起见,汽车可以视为在数轴上移动的点。当两辆车占据相同位置时就会发生碰撞。一旦一辆车与另一辆车发生碰撞,它们就会合并形成一个车队。车队中的汽车将拥有相同的位置和相同的速度,即车队中最慢汽车的初始速度。
返回数组 answer,其中 answer[i] 是第 i 辆车与下一辆车发生碰撞的时间(秒),如果该车不会与下一辆车发生碰撞,则为 -1。与实际答案误差在 10^-5 以内的答案都可以接受。
示例 1:
输入:cars = [[1,2],[2,1],[4,3],[7,2]]
输出:[1.00000,-1.00000,3.00000,-1.00000]
解释:恰好经过1秒后,第一辆车将与第二辆车发生碰撞,形成一个速度为1米/秒的车队。恰好经过3秒后,第三辆车将与第四辆车发生碰撞,形成一个速度为2米/秒的车队。
示例 2:
输入:cars = [[3,4],[5,4],[6,3],[9,1]]
输出:[2.00000,1.00000,1.50000,-1.00000]
约束:
- 1 <= cars.length <= 10^5
- 1 <= positioni, speedi <= 10^6
- positioni < positioni+1
解题思路
这道题的核心思想是使用单调栈来维护候选的碰撞目标车辆。
分析过程:
基本碰撞条件:车 i 能追上车 j(i < j)的条件是 speed[i] > speed[j],碰撞时间为 (position[j] - position[i]) / (speed[i] - speed[j])。
关键洞察:我们从右往左遍历,对于当前车辆,需要找到它能碰撞的第一辆车。但有些车辆可能永远不会成为碰撞目标,需要优化掉。
单调栈优化:
- 如果右侧有两辆车 A 和 B(A 在 B 左边),且当前车能在碰撞 B 之前先碰撞 A,那么 A 会"屏蔽"B
- 我们维护一个栈,确保栈中的车辆按照碰撞时间单调递增
- 对于当前车辆,先移除速度不小于当前车的车辆(无法碰撞)
- 然后移除会被"屏蔽"的车辆
具体步骤:
- 从右往左遍历每辆车
- 维护一个栈存储可能的碰撞目标
- 对于每辆车,清理栈中不合适的车辆
- 计算与栈顶车辆的碰撞时间
推荐解法:使用单调栈,时间复杂度 O(n),是最优解法。
代码实现
class Solution {
public:
vector<double> getCollisionTimes(vector<vector<int>>& cars) {
int n = cars.size();
vector<double> result(n, -1.0);
vector<int> stack;
auto getCollisionTime = [&](int i, int j) -> double {
return (double)(cars[j][0] - cars[i][0]) / (cars[i][1] - cars[j][1]);
};
for (int i = n - 1; i >= 0; i--) {
// 移除速度大于等于当前车的车辆
while (!stack.empty() && cars[stack.back()][1] >= cars[i][1]) {
stack.pop_back();
}
// 移除会被屏蔽的车辆
while (stack.size() > 1) {
int j = stack.back();
int k = stack[stack.size() - 2];
if (getCollisionTime(i, j) <= getCollisionTime(j, k)) {
break;
}
stack.pop_back();
}
if (!stack.empty()) {
result[i] = getCollisionTime(i, stack.back());
}
stack.push_back(i);
}
return result;
}
};
class Solution:
def getCollisionTimes(self, cars: List[List[int]]) -> List[float]:
n = len(cars)
result = [-1.0] * n
stack = []
def get_collision_time(i, j):
return (cars[j][0] - cars[i][0]) / (cars[i][1] - cars[j][1])
for i in range(n - 1, -1, -1):
# 移除速度大于等于当前车的车辆
while stack and cars[stack[-1]][1] >= cars[i][1]:
stack.pop()
# 移除会被屏蔽的车辆
while len(stack) > 1:
j, k = stack[-1], stack[-2]
if get_collision_time(i, j) <= get_collision_time(j, k):
break
stack.pop()
if stack:
result[i] = get_collision_time(i, stack[-1])
stack.append(i)
return result
public class Solution {
public double[] GetCollisionTimes(int[][] cars) {
int n = cars.Length;
double[] result = new double[n];
for (int i = 0; i < n; i++) {
result[i] = -1.0;
}
Stack<int> stack = new Stack<int>();
double GetCollisionTime(int i, int j) {
return (double)(cars[j][0] - cars[i][0]) / (cars[i][1] - cars[j][1]);
}
for (int i = n - 1; i >= 0; i--) {
// 移除速度大于等于当前车的车辆
while (stack.Count > 0 && cars[stack.Peek()][1] >= cars[i][1]) {
stack.Pop();
}
// 移除会被屏蔽的车辆
while (stack.Count > 1) {
int j = stack.Peek();
stack.Pop();
int k = stack.Peek();
stack.Push(j);
if (GetCollisionTime(i, j) <= GetCollisionTime(j, k)) {
break;
}
stack.Pop();
}
if (stack.Count > 0) {
result[i] = GetCollisionTime(i, stack.Peek());
}
stack.Push(i);
}
return result;
}
}
var getCollisionTimes = function(cars) {
const n = cars.length;
const result = new Array(n).fill(-1.0);
const stack = [];
const getCollisionTime = (i, j) => {
return (cars[j][0] - cars[i][0]) / (cars[i][1] - cars[j][1]);
};
for (let i = n - 1; i >= 0; i--) {
// 移除速度大于等于当前车的车辆
while (stack.length > 0 && cars[stack[stack.length - 1]][1] >= cars[i][1]) {
stack.pop();
}
// 移除会被屏蔽的车辆
while (stack.length > 1) {
const j = stack[stack.length - 1];
const k = stack[stack.length - 2];
if (getCollisionTime(i, j) <= getCollisionTime(j, k)) {
break;
}
stack.pop();
}
if (stack.length > 0) {
result[i] = getCollisionTime(i, stack[stack.length - 1]);
}
stack.push(i);
}
return result;
};
复杂度分析
| 复杂度类型 | 分析 |
|---|---|
| 时间复杂度 | O(n),每辆车最多被压入和弹出栈一次 |
| 空间复杂度 | O(n),栈的最大大小为 n |
相关题目
. Car Fleet (Medium)
. Count Collisions on a Road (Medium)