2940 单调栈+更大原理

class Solution {
public:
        vector<int> solve(vector<int>& nums) {
  int n = nums.size();
  vector<int> ret(n, -1);
  stack<int> st;
  for (int i = 0; i < n; ++i) {
    while (!st.empty() && nums[i] > nums[st.top()]) {
      ret[st.top()] = i;
      st.pop();
    }
    st.push(i);
  }
  return ret;
}
    vector<int> leftmostBuildingQueries(vector<int>& heights, vector<vector<int>>& queries) {
        //如果两个人一开始就在同一个建筑,返回当前数组下标
        //创建一个临时数组 最后返回它
        //使用单调栈法

vector<int> greaterArray = solve(heights);//获得第一个比他大的下标.

        vector<int> v;
        for(int i=0;i<queries.size();i++)
        {
            //如果两人下标相同  不超过heights长度
            if (queries[i][0] == queries[i][1]&&queries[i][0]<heights.size())
            {            
               v.push_back(queries[i][0]);  
               //同时进入到下一个分支 以后尽量少使用goto语句 容易造成意想不到的错误
            }
            else
            {
                //确定queries中更大的数 作为数组下标
            //greaterNum = ;
            //如果可以直接跳转到比较大的身上
            if(heights[max(queries[i][0],queries[i][1])]>heights[min(queries[i][0],queries[i][1])])
            {
                v.push_back(max(queries[i][0],queries[i][1]));
            }
            else
            {
                if(greaterArray[queries[i][0]]!=-1&&greaterArray[queries[i][1]]!=-1)
                {
                    // if(heights[greaterArray[max(queries[i][0],queries[i][1])]]>heights[min(queries[i][0],queries[i][1])])
                    // v.push_back(greaterArray[max(queries[i][0],queries[i][1])]);
                    int j = max(greaterArray[queries[i][0]],greaterArray[queries[i][1]]);//更大的第一个数
                    for(;heights[j]<heights[min(queries[i][0],queries[i][1])];)
                    {
                        j = greaterArray[j];
                        if(j == -1)
                        {
                            break;
                        }
                    }
                    v.push_back(j);
                }

                //  else -1
               else
               v.push_back(-1);
            }
               }
        }

    return v;

    }
};

2940. 找到 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 。

示例 1:

输入:heights = [6,4,8,5,2,7], queries = [[0,1],[0,3],[2,4],[3,4],[2,2]]
输出:[2,5,-1,5,2]
解释:第一个查询中,Alice 和 Bob 可以移动到建筑 2 ,因为 heights[0] < heights[2] 且 heights[1] < heights[2] 。
第二个查询中,Alice 和 Bob 可以移动到建筑 5 ,因为 heights[0] < heights[5] 且 heights[3] < heights[5] 。
第三个查询中,Alice 无法与 Bob 相遇,因为 Alice 不能移动到任何其他建筑。
第四个查询中,Alice 和 Bob 可以移动到建筑 5 ,因为 heights[3] < heights[5] 且 heights[4] < heights[5] 。
第五个查询中,Alice 和 Bob 已经在同一栋建筑中。
对于 ans[i] != -1 ,ans[i] 是 Alice 和 Bob 可以相遇的建筑中最左边建筑的下标。
对于 ans[i] == -1 ,不存在 Alice 和 Bob 可以相遇的建筑。

示例 2:

输入:heights = [5,3,8,2,6,1,4,6], queries = [[0,7],[3,5],[5,2],[3,0],[1,6]]
输出:[7,6,-1,4,6]
解释:第一个查询中,Alice 可以直接移动到 Bob 的建筑,因为 heights[0] < heights[7] 。
第二个查询中,Alice 和 Bob 可以移动到建筑 6 ,因为 heights[3] < heights[6] 且 heights[5] < heights[6] 。
第三个查询中,Alice 无法与 Bob 相遇,因为 Bob 不能移动到任何其他建筑。
第四个查询中,Alice 和 Bob 可以移动到建筑 4 ,因为 heights[3] < heights[4] 且 heights[0] < heights[4] 。
第五个查询中,Alice 可以直接移动到 Bob 的建筑,因为 heights[1] < heights[6] 。
对于 ans[i] != -1 ,ans[i] 是 Alice 和 Bob 可以相遇的建筑中最左边建筑的下标。
对于 ans[i] == -1 ,不存在 Alice 和 Bob 可以相遇的建筑。

提示:

  • 1 <= heights.length <= 5 * 104
  • 1 <= heights[i] <= 109
  • 1 <= queries.length <= 5 * 104
  • queries[i] = [ai, bi]
  • 0 <= ai, bi <= heights.length - 1

官方题解:

方法一:线段树
思路与算法

由题意可知,人只能往右边移动,并且两人交换位置后不影响答案。对于每一次询问不妨设 a 
i <b i 。如果 heights[a i]<heights[b i​ ],那么答案就是 b i,否则答案在 b i右边。

令 hights 的长度为 n,问题转化为在区间 [b i+1,n] 中找到最左边的下标 x 满足 heights[x]>heights[b i],对于此问题用线段树求解即可。

class Solution {
public:
    vector<int> zd;

    void build(int l, int r, int rt, vector<int> &heights) {
        if (l == r) {
            zd[rt] = heights[l - 1];
            return;
        }

        int mid = (l + r) >> 1;
        build(l, mid, rt << 1, heights);
        build(mid + 1, r, rt << 1 | 1, heights);
        zd[rt] = max(zd[rt << 1], zd[rt << 1 | 1]);
    }

    int query(int pos, int val, int l, int r, int rt) {
        if (val >= zd[rt]) {
            return 0;
        }

        if (l == r) {
            return l;
        }

        int mid = (l + r) >> 1;
        if (pos <= mid) {
            int res = query(pos, val, l, mid, rt << 1);
            if (res != 0) {
                return res;
            }
        }
        return query(pos, val, mid + 1, r, rt << 1 | 1);
    }

    vector<int> leftmostBuildingQueries(vector<int>& heights, vector<vector<int>>& queries) {
        int n = heights.size();
        zd.resize(n * 4);
        build(1, n, 1, heights);

        int m = queries.size();
        vector<int> ans(m);
        for (int i = 0; i < m; i++) {
            int a = queries[i][0];
            int b = queries[i][1];
            if (a > b) {
                swap(a, b);
            }

            if (a == b || heights[a] < heights[b]) {
                ans[i] = b;
                continue;
            }

            ans[i] = query(b + 1, heights[a], 1, n, 1) - 1;
        }
        return ans;
    }
};


复杂度分析

时间复杂度:O(mlogn),其中n 表示数组 heights 的长度,m 表示查询的长度。

空间复杂度:O(n)。

方法二:离线 + 二分单调栈
思路与算法

和方法一相同,先对每一次查询进行预处理使得 a<b。如果 heights[a]<heights[b],那么可直接得到答案为 b。假设 heights[a]>=heights[b],那么答案一定在 b 的右边。可以将查询离线处理,从后往前枚举,维护一个从大到小的单调栈 st。对于每一个 b,在单调栈中二分找到第一个大于 heights[a] 的即可。

class Solution {
public:
    vector<int> leftmostBuildingQueries(vector<int>& heights, vector<vector<int>>& queries) {
        int n = heights.size();
        int m = queries.size();
        vector<vector<pair<int, int>>> query(n);
        vector<int> ans(m);
        vector<int> st;

        for (int i = 0; i < m; i++) {
            int a = queries[i][0];
            int b = queries[i][1];
            if (a > b) swap(a, b);
            if (a == b || heights[a] < heights[b]) {
                ans[i] = b;
                continue;
            }
            query[b].push_back(make_pair(i, heights[a]));
        }

        int top = -1;
        for (int i = n - 1; i >= 0; i--) {
            for (int j = 0; j < query[i].size(); j++) {
                int q = query[i][j].first;
                int val = query[i][j].second;
                if (top == -1 || heights[st[0]] <= val) {
                    ans[q] = -1;
                    continue;
                }

                int l = 0, r = top;
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (heights[st[mid]] > val) {
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
                ans[q] = st[r];
            }

            while (top >= 0 && heights[st[top]] <= heights[i]) {
                st.pop_back();
                top--;
            }
            st.push_back(i);
            top++;
        }
        return ans;
    }
};


复杂度分析

时间复杂度:O(mlogn),其中n 表示数组 heights 的长度,m 表示查询的长度。

空间复杂度:O(n+m),其中单调栈最长为 n,查询最多有 m 个。

作者:力扣官方题解
链接:https://leetcode.cn/problems/find-building-where-alice-and-bob-can-meet/solutions/2872957/zhao-dao-alice-he-bob-ke-yi-xiang-yu-de-x5i0t/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

树状数组、并查集和单调栈是常用的数据结构,它们在算法和数据处理中有着广泛的应用。 1. 树状数组(Binary Indexed Tree)是一种用于高效处理区间和的数据结构。它可以在O(logN)的时间内完成单点更新和区间查询操作。树状数组主要用于解决一些前缀和相关的问题,例如计算数组的前缀和、区间和等。树状数组的基本原理是利用二进制表示的索引来维护和查询区间和。具体操作包括更新操作和查询操作。 2. 并查集(Union-Find Set)是一种用于处理不相交集合的数据结构。它支持合并集合和查询元素所在集合的操作。并查集的基本原理是使用树形结构来表示集合,每个节点储存其父节点的信息。通过路径压缩和按秩合并的优化策略,可以实现较快的合并和查询操作。并查集常用于解决一些连通性问题,例如判断图中的连通分量、判断两个元素是否在同一个集合中等。 3. 单调栈(Monotonic Stack)是一种特殊的栈数据结构,它可以在O(N)的时间内解决一些与单调性相关的问题。单调栈的基本原理是维护一个单调递增或单调递减的栈,通过比较栈顶元素和当前元素的大小关系来确定栈中元素的位置。单调栈常用于解决一些与最大值、最小值、下一个更大元素、下一个更小元素等相关的问题。 以下是对树状数组、并查集和单调栈的简单介绍。如果你对其中某个数据结构感兴趣,我可以为你提供更详细的解释和示例代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值