千问 LeetCode 2940. 找到 Alice 和 Bob 可以相遇的建筑 TypeScript实现
这道题的 TypeScript 实现可以沿用之前 Java 版本中“离线查询 + 单调栈”的核心思路。
💡 核心思路
1. 预处理查询(离线化):遍历所有查询,如果 Alice 和 Bob 在同一个建筑,或者其中一个在另一个的右侧且高度更高,那么他们可以直接在右侧的建筑相遇。对于无法直接相遇的情况,我们需要记录他们必须去右侧寻找的“最低高度要求”和“起始索引”,并将这些待处理的查询按照起始索引从大到小排序。
2. 单调栈维护右侧最优解:从右向左遍历建筑数组。维护一个单调递减栈(存储建筑索引),栈内的建筑高度是单调递减的。因为对于左侧的人来说,右侧一个“又高又靠左”的建筑永远比“又矮又靠右”的建筑更优。
3. 二分查找满足条件的建筑:当遍历到某个位置时,处理所有需要在该位置右侧相遇的查询。由于单调栈中的建筑高度是单调递减的,我们可以直接在栈中对高度进行二分查找,找到第一个满足高度要求的建筑,即为最靠左的相遇点。
💻 TypeScript 代码实现
function leftmostBuildingQueries(heights: number[], queries: number[][]): number[] {
const n = heights.length;
const m = queries.length;
const ans: number[] = new Array(m).fill(-1);
// 用于存储需要离线处理的查询
// 格式为 [必须大于的索引位置, 必须大于的高度, 原始查询索引]
const sortedQueries: [number, number, number][] = [];
// 1. 预处理查询
for (let i = 0; i < m; i++) {
let a = queries[i][0];
let b = queries[i][1];
// 保证 a <= b,方便后续处理
if (a > b) {
[a, b] = [b, a];
}
// 情况1:两人在同一建筑,或者右边的人本身就比左边的人高,直接在 b 相遇
if (a === b || heights[b] > heights[a]) {
ans[i] = b;
} else {
// 情况2:需要去 b 的右边找更高的楼,记录待处理查询
sortedQueries.push([b, Math.max(heights[a], heights[b]), i]);
}
}
// 按照“必须大于的索引位置”从大到小排序
sortedQueries.sort((x, y) => y[0] - x[0]);
// 2. 单调栈 + 离线处理
// stack 存储索引,对应的 heights 是单调递减的
const stack: number[] = [];
let qIdx = 0;
// 从右向左遍历每一个建筑
for (let i = n - 1; i >= 0; i--) {
// 维护单调栈:把比当前楼矮(或相等)的右边楼都弹出去(当前楼更优)
while (stack.length > 0 && heights[stack[stack.length - 1]] <= heights[i]) {
stack.pop();
}
stack.push(i);
// 处理所有需要在 i 右侧相遇的查询
while (qIdx < sortedQueries.length && sortedQueries[qIdx][0] === i) {
const targetHeight = sortedQueries[qIdx][1];
const originalIdx = sortedQueries[qIdx][2];
// 在单调栈中二分查找第一个高度 > targetHeight 的楼
// stack 中的索引对应的建筑高度是单调递减的(stack[0]最高,stack[length-1]最矮)
// 我们要找最靠左(索引最小)且高度满足条件的,也就是在 stack 中尽可能靠后的位置
let left = 0;
let right = stack.length - 1;
let res = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (heights[stack[mid]] > targetHeight) {
res = stack[mid]; // 这是一个可行解
left = mid + 1; // 尝试在更靠后的位置(更靠左的建筑)寻找
} else {
right = mid - 1; // 高度不够,需要往更靠前的位置(更高的建筑)找
}
}
ans[originalIdx] = res;
qIdx++;
}
}
return ans;
}
📝 复杂度分析
* 时间复杂度:O((n + m) log n)。预处理和排序查询需要 O(m log m);遍历建筑是 O(n),每个建筑最多进栈出栈一次;对于每个需要离线处理的查询,我们在单调栈中进行二分查找,单次耗时 O(log n)。
* 空间复杂度:O(n + m)。需要额外的空间来存储单调栈以及离线处理的查询数组。
更多推荐

所有评论(0)