【leetcode题解】(二分单调栈)找到 Alice 和 Bob 可以相遇的建筑

题目描述

给你一个下标从 0 开始的正整数数组 heights ,其中 heights[i] 表示第 i 栋建筑的高度。

如果一个人在建筑 i ,且存在 i < j 的建筑 j 满足 heights[i] < heights[j] ,那么这个人可以移动到建筑 j

给你另外一个数组 queries ,其中 queries[i] = [ai, bi] 。第 i 个查询中,Alice 在建筑 ai ,Bob 在建筑 bi

请你能返回一个数组 ans ,其中 ans[i] 是第 i 个查询中,Alice 和 Bob 可以相遇的 最左边的建筑 。如果对于查询 i ,Alice 和 Bob 不能相遇,令 ans[i]-1

单调栈

单调栈中存放的数据是有序的,所以单调栈也分为单调递增栈单调递减栈

以单调递增栈为例,如果栈为空入栈元素值大于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素大的元素全部出栈。单调递减栈反之。

以下是一个单调栈的伪代码:

stack<int> st;
for(遍历数组){
	if(栈空 || 入栈元素小于栈顶元素){
        push;
    } else{
        while(栈顶不为空 && 入站元素大于栈顶元素){
            pop;
            更新栈内结果
        }
        push;
    }
}

当然单调栈是一种解题思路,不一定是Stack。也可是List。

下面是单调递增栈的模板:

int[] left = new int[n];  // 存的是下标
Deque<Integer> st = new ArrayDeque<>();  // 单调递增的栈
for (int i = 0; i < n; i++) {  // 从左向右遍历
    int x = heights[i];
    while (!st.isEmpty() && x <= heights[st.peek()]) {  // 当前元素要入栈,所有比它大的都不要
        st.pop();
    }
    // 先赋值再压栈,确保每次得到的是开区间
    left[i] = st.isEmpty() ? -1 : st.peek();
    st.push(i);
    
作者:wxyz
链接:https://leetcode.cn/problems/find-building-where-alice-and-bob-can-meet/solutions/2875204/chi-xian-cha-xun-zui-xiao-dui-dan-diao-z-pbsq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

题解

class Solution {
    public int[] leftmostBuildingQueries(int[] heights, int[][] queries) {
        // 离线查询+单调栈+二分
        int n = heights.length;
        int[] ans = new int[queries.length];
        // 第i组询问,存储[两人靠后的那个-(第i组高度,第i次查询)]
        List<int[]>[] qs = new ArrayList[n];
        Arrays.setAll(qs, i -> new ArrayList<>());
        // 预处理--离线
        for (int i = 0; i < queries.length; i++) {
            int a = queries[i][0];
            int b = queries[i][1];
            if (a > b) {  // 保证 a <= b
                int tmp = a;
                a = b;
                b = tmp;
            }
            if (a == b || heights[a] < heights[b]) {
                ans[i] = b;  // a 能直接跳到 b
            } else {  // 离线存储,存储最大高度,存到靠后的那组
                qs[b].add(new int[]{heights[a], i});
            }
        }
        // 单调栈处理--在线
        int[] st = new int[n];
        int top = 0;
        for (int i = n - 1; i >= 0; i--) {  // 倒序遍历
            for (int[] q : qs[i]) {  // 二分找到答案
                ans[q[1]] = binarySearch(heights, st, top, q[0]);
            }
            // 更新栈顶元素,确保栈单调递减
            while (top > 0 && heights[i] >= heights[st[top - 1]]) {
                top--;
            }
            st[top++] = i;
        }
        return ans;
    }

    // 返回 st 中最后一个 > x 的高度的下标
    // 如果不存在,返回 -1
    private int binarySearch(int[] heights, int[] st, int right, int x) {
        // 记录二分的循环不变量
        int left = -1; // 开区间 (left, right)
        while (left + 1 < right) { // 开区间不为空
            int mid = (left + right) >>> 1;
            if (heights[st[mid]] > x) {
                left = mid; // 范围缩小到 (mid, right)
            } else {
                right = mid; // 范围缩小到 (left, mid)
            }
        }
        return left < 0 ? -1 : st[left];
    }
}

作者:wxyz
链接:https://leetcode.cn/problems/find-building-where-alice-and-bob-can-meet/solutions/2875204/chi-xian-cha-xun-zui-xiao-dui-dan-diao-z-pbsq/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本博客用作个人笔记使用,各种问题不甚清晰,可前往引用的作者链接学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值