【leetcode:1944. 队列中可以看到的人数】单调栈算法及其相关问题

这篇具有很好参考价值的文章主要介绍了【leetcode:1944. 队列中可以看到的人数】单调栈算法及其相关问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1944. 队列中可以看到的人数

题目

有 n 个人排成一个队列,从左到右 编号为 0 到 n - 1 。给你以一个整数数组 heights ,每个整数 互不相同heights[i] 表示第 i 个人的高度。

一个人能 看到 他右边另一个人的条件是这两人之间的所有人都比他们两人  。更正式的,第 i 个人能看到第 j 个人的条件是 i < j 且 min(heights[i], heights[j]) > max(heights[i+1], heights[i+2], ..., heights[j-1]) 。

请你返回一个长度为 n 的数组 **answer **,其中 **answer[i] **是第 i 个人在他右侧队列中能 看到 的 人数 。

原始想法

  • 对于序号为i的人,右侧第一个高度大于等于他的人序号为j,那么j右侧的人,i一定无法看到了。
  • 那只需要判断ij之间的人

右侧第一个高度大于等于他的人”,这句话让人很自然的想到单调栈,因为单调栈的常见适用场景就是寻找左(右)侧第一个比当前元素大(小)的元素

利用单调栈找到i右侧第一个高度大于等于他的j后,如何判断i是否能看到中间的某个人k呢?按照题目描述,ik之间的所有人都比他们两人,这用遍历就能解决,但时间复杂度显然比较高,有没有办法优化一下?

可以再利用单调栈,寻找每个人左侧第一个高度大于等于他的人,如果k左边第一个高度大于等于他的人位置小于等于i,说明ik之间的人高度都小于他们,也就是满足题目中被看到的条件。

总之,最终的思路是:

  • 利用单调栈,找到每个人左边和右边第一个高度大于等于他的人,
    • 假设l[i]表示i左侧第一个高度大于等于他的人的位置,r[i]表示i右侧第一个高度大于等于他的人的位置
  • 对于i来说,遍历ir[i]之间的所有元素,比如i < k ≤ r[i],如果l[k] ≤ i,那么i就能看到k。

代码如下:

class Solution {
public:
    vector<int> canSeePersonsCount(vector<int>& heights) {
        int len = heights.size();
        stack<int> sl = stack<int>();
        stack<int> sr = stack<int>();
        vector<int> l = vector<int>(len, -1); // 左边第一个高度大于等于
        vector<int> r = vector<int>(len, len);
        // 利用单调栈寻找l
        for(int i = 0;i < len;++i) {
            while(!sl.empty() && heights[i] > heights[sl.top()]) {
                sl.pop();
            }
            l[i] = sl.empty() ? -1 : sl.top();
            sl.push(i);
        }
        // 利用单调栈寻找r
        for(int i = len - 1;i >= 0;--i) {
            while(!sr.empty() && heights[i] > heights[sr.top()]) {
                sr.pop();
            }
            r[i] = sr.empty() ? len : sr.top();
            sr.push(i);
        }
        
        vector<int> res(len, 0);
        for(int i = 0;i < len - 1;++i) {
            int j = i + 1;
            for(;j < r[i];++j) {
                if(l[j] <= i) {
                    ++res[i];
                }
            }
            if(j != len) {
                ++res[i];
            }
        }
        return res;
    }
};

利用单调栈计算lr的时间复杂度是 O ( n ) O(n) O(n),针对每个人又遍历判断是否满足“看到”的要求,所以总体时间复杂度是 O ( n 2 ) O(n^2) O(n2) 。提交结果是对于某些情况超时,说明这种解决方案的时间复杂度还是太高了

问题出在哪里呢?

最终方案

深究利用单调栈计算lr)的过程:对于某个元素i,如果栈顶元素高度小于i的高度,就把栈顶元素弹出,直到栈顶元素高度大于等于他的高度或者栈空。元素i入栈

这个过程中,弹出的元素和元素i之间一定满足“两者之间的元素高度都小于两者”,否则当前栈顶元素在之前就会被出栈。

所以,其实在上述过程中弹出的元素就是i能够看到的人,因此原始想法中的遍历过程是重复计算的。

最终代码如下:

class Solution {
public:
    vector<int> canSeePersonsCount(vector<int>& heights) {
        int len = heights.size();
        stack<int> s = stack<int>();
        vector<int> res(len, 0);
        for(int i = len - 1;i >= 0;--i) {
            while(!s.empty() && heights[i] > heights[s.top()]) {
                ++res[i];
                s.pop();
            }
            if(!s.empty()) {
                ++res[i];
            }
            s.push(i);
        }
        return res;
    }
};

这样时间复杂度其实就只有 O ( n ) O(n) O(n)了,理所应当地通过了。

单调栈知识点及相关题目

单调栈知识点可以参考:https://oi-wiki.org/ds/monotonous-stack/

leetcode上其实有很多单调栈相关题目,比如剑指 Offer II 039. 直方图最大矩形面积等。文章来源地址https://www.toymoban.com/news/detail-822953.html

到了这里,关于【leetcode:1944. 队列中可以看到的人数】单调栈算法及其相关问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 前缀和+单调双队列+贪心:LeetCode2945:找到最大非递减数组的长度

    前缀和+单调双队列+贪心:LeetCode2945:找到最大非递减数组的长度

    C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 单调双队列 贪心 给你一个下标从 0 开始的整数数组 nums 。 你可以执行任意次操作。每次操作中,你需要选择一个 子数组 ,并将这个子数组用它所包含元素的 和 替换。比方说,给定数组是 [1,3,

    2024年02月03日
    浏览(9)
  • 算法设计-单调队列

    这个东西我弄得还不是很明白,所以先放一篇博客:https://www.cnblogs.com/I-Love-You-520/p/13454305.html ​ 单调队列说的就是博客中提到的场景,可以说解决的是 固定长度,多次查询一段的最值 (其实就是slice window的意思),这种如果是朴素算法的话,因为查一段序列的最值的时间复

    2023年04月08日
    浏览(13)
  • 剑指 Offer 59 - I. 滑动窗口的最大值 / LeetCode 239. 滑动窗口最大值(优先队列 / 单调队列)

    链接:剑指 Offer 59 - I. 滑动窗口的最大值;LeetCode 239. 滑动窗口最大值 难度:困难 下一篇:剑指 Offer 59 - II. 队列的最大值(单调队列) 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗

    2024年02月15日
    浏览(11)
  • 【二】一起算法---队列:STL queue、手写循环队列、双端队列和单调队列、优先队列

    【二】一起算法---队列:STL queue、手写循环队列、双端队列和单调队列、优先队列

    纸上得来终觉浅,绝知此事要躬行。大家好!我是霜淮子,欢迎订阅我的专栏《算法系列》。 学习经典算法和经典代码,建立算法思维;大量编码让代码成为我们大脑的一部分。 ⭐️已更系列  1、基础数据结构        1.1、链表➡传送门        1.2、队列➡本章 专栏直达

    2023年04月08日
    浏览(12)
  • 【算法题解】23. 「滑动窗口最大值」单调队列解法

    【算法题解】23. 「滑动窗口最大值」单调队列解法

    这是一道 困难 题 题目来自:https://leetcode.cn/problems/sliding-window-maximum/ 给你一个整数数组 nums ,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1: 示

    2023年04月11日
    浏览(10)
  • 算法训练day37|贪心算法 part06(LeetCode738.单调递增的数字)

    题目链接🔥🔥 给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。 (当且仅当每个相邻位数上的数字 x 和 y 满足 x = y 时,我们称这个整数是单调递增的。) 示例 1: 输入: N = 10 输出: 9 示例 2: 输入: N = 1234 输出:

    2024年02月09日
    浏览(11)
  • 算法打卡day32|贪心算法篇06|Leetcode 738.单调递增的数字、968.监控二叉树

    算法打卡day32|贪心算法篇06|Leetcode 738.单调递增的数字、968.监控二叉树

    Leetcode 738.单调递增的数字 题目链接:738.单调递增的数字  大佬视频讲解:单调递增的数字视频讲解  个人思路 这个题目就是从例子中找规律,例如 332,从后往前遍历,32不是单调递增将2变为9,3减1,变成了329,遍历到2,32不是递增,将2变为9,3减1,变成299,符合题目条件,打印

    2024年04月16日
    浏览(8)
  • 【LeetCode题目详解】第八章 贪心算法 part06 738.单调递增的数字 968.监控二叉树 (day37补)

    【LeetCode题目详解】第八章 贪心算法 part06 738.单调递增的数字 968.监控二叉树 (day37补)

    当且仅当每个相邻位数上的数字  x  和  y  满足  x = y  时,我们称这个整数是 单调递增 的。 给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。 示例 1: 示例 2: 示例 3: 提示: 0 = n = 109 # 暴力解法 题意很简单,那么首先想的就是暴力解法了,来我替大家

    2024年02月10日
    浏览(13)
  • 【单调队列】 单调队列的“扫描线”理解

    【单调队列】 单调队列的“扫描线”理解

       “如果一个选手比你小还比你强,你就可以退役了。”——单调队列的原理 比你强,而且比你影响时间更长。 某种意义上,数学思维是生活中的思考的延伸。   算法学习笔记(66): 单调队列。引用 Pecco 的算法笔记。   在这里给出一种扫描线的理解。   我们以滑动

    2024年02月12日
    浏览(14)
  • C++ 单调队列 || 单调队列模版题:滑动窗口

    给定一个大小为 n≤106 的数组。 有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。 你只能在窗口中看到 k 个数字。 每次滑动窗口向右移动一个位置。 以下是一个例子: 该数组为 [1 3 -1 -3 5 3 6 7],k 为 3 。 窗口位置 最小值 最大值 [1 3 -1] -3 5 3 6 7 -1 3 1 [3 -1 -3] 5

    2024年01月20日
    浏览(12)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包