算法日记day 46(单调栈之下一个更大元素|柱状图中最大图形)

news2024/11/24 8:31:01

一、下一个更大元素1

题目:

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

示例 1:

输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。

示例 2:

输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
- 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。

思路:

解法有许多种,这里采用单调栈和哈希表的解法,将数组nums1中的元素和索引全部存入哈希表中,遍历数组nums2,如果栈顶元素小于遍历到的元素,弹出该元素并与map比较有无相同元素,如果有则更新下标为对应的位置

代码:

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    // 创建一个哈希表,用于存储 nums1 中每个元素及其索引
    HashMap<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums1.length; i++) {
        map.put(nums1[i], i);
    }

    // 创建结果数组,初始化为 -1
    int[] res = new int[nums1.length];
    Arrays.fill(res, -1);

    // 使用栈来跟踪 nums2 中的元素
    Stack<Integer> stack = new Stack<>();

    // 遍历 nums2
    for (int i = 0; i < nums2.length; i++) {
        // 当栈不为空且栈顶元素小于当前元素时
        while (!stack.isEmpty() && nums2[stack.peek()] < nums2[i]) {
            // 弹出栈顶元素,并更新结果数组
            int pre = nums2[stack.pop()];
            // 如果弹出的元素存在于 nums1 中
            if (map.containsKey(pre)) {
                res[map.get(pre)] = nums2[i];
            }
        }
        // 将当前元素的索引压入栈中
        stack.push(i);
    }

    return res;
}

这个哈希表用于存储 nums1 中每个元素及其对应的索引位置,以便快速查找。

遍历 nums1,将每个元素及其索引添加到哈希表中。

创建一个与 nums1 长度相同的结果数组 res,并用 -1 初始化,表示默认情况下每个元素的下一个更大元素是 -1

使用栈来存储 nums2 中的索引,以帮助跟踪当前元素和候选更大元素的比较。

如果栈不为空且栈顶元素小于当前元素,则弹出栈顶元素,检查它是否在 nums1 中。如果是,则更新结果数组中的对应位置。

将当前元素的索引压入栈中,以便后续元素可以用来比较。

暴力解法

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    // 创建一个与 nums1 长度相同的结果数组 ans,初始值为 -1
    int[] ans = new int[nums1.length];
    for (int i = 0; i < ans.length; i++) {
        ans[i] = -1; // 默认情况下,所有结果初始化为 -1
    }

    // 遍历 nums1 中的每个元素
    for (int i = 0; i < nums1.length; i++) {
        int num = nums1[i]; // 当前要寻找下一个更大元素的数字
        
        // 遍历 nums2 来寻找 num 的下一个更大元素
        for (int j = 0; j < nums2.length; j++) {
            // 找到 num 在 nums2 中的位置
            if (nums2[j] == num) {
                int k = j + 1; // 从 num 的下一个位置开始查找
                
                // 遍历 nums2 中 num 之后的元素
                while (k < nums2.length) {
                    // 如果找到比 num 更大的元素
                    if (nums2[k] > num) {
                        ans[i] = nums2[k]; // 更新结果数组
                        break; // 找到下一个更大元素后退出循环
                    }
                    k++; // 否则继续查找
                }
                break; // 找到 num 后退出内层循环
            }
        }
    }
    return ans; // 返回结果数组
}

 

二、下一个更大元素2

题目:

给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。

示例 1:

输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数; 
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

示例 2:

输入: nums = [1,2,3,4,3]
输出: [2,3,4,-1,4]

思路:

本题的特点是数组是一个环形结构,数组中最后一个数的值需要由第二次遍历数组来确定,因此可以将数组进行复制拉长,同时超出数组长度的下标索引采用取模的方法进行定位

代码:

public int[] nextGreaterElements(int[] nums) {
    // 边界判断:如果 nums 为空或长度小于等于1,直接返回 -1
    if (nums == null || nums.length <= 1) {
        return new int[] { -1 };
    }
    
    int size = nums.length; // 数组的长度
    int[] result = new int[size]; // 结果数组,存放每个元素的下一个更大元素
    Arrays.fill(result, -1); // 初始化结果数组为 -1,表示默认没有找到更大的元素
    
    Stack<Integer> st = new Stack<>(); // 栈,用于存放 nums 数组的下标

    // 遍历数组 nums 两遍以处理循环的情况
    for (int i = 0; i < 2 * size; i++) {
        // 栈不为空且当前元素大于栈顶元素对应的元素
        while (!st.empty() && nums[i % size] > nums[st.peek()]) {
            result[st.peek()] = nums[i % size]; // 更新结果数组
            st.pop(); // 弹出栈顶元素
        }
        st.push(i % size); // 将当前元素的下标入栈
    }
    
    return result; // 返回结果数组
}

如果输入数组 numsnull 或者数组长度小于等于 1,直接返回一个结果数组 [-1]。因为对于空数组或单元素数组来说,不可能存在下一个更大的元素。

size 存储数组 nums 的长度。result 数组用于存储每个元素的下一个更大元素,初始时用 -1 表示默认没有找到更大的元素。st 是一个栈,用于存储 nums 中元素的下标。

通过遍历 2 * size 的范围来处理循环的情况。这种方法确保每个元素在循环后的数组中都有机会被检查一次。

nums[i % size] 使用模运算来处理数组的循环性质。

当栈不为空且当前元素 nums[i % size] 大于栈顶元素对应的值时,更新 result 数组,并将栈顶元素弹出。

将当前元素的下标入栈,以便后续可以找到比它大的元素。

返回存储了每个元素下一个更大元素的结果数组。

栈中元素的动态分析

以例子

输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数; 
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

初始化

  • 输入数组 nums = [1,2,1]
  • 结果数组 result = [-1, -1, -1](初始状态,所有值为 -1
  • 栈 st 为空

遍历过程

我们遍历 2 * size 次,其中 sizenums 的长度。对于输入 nums = [1,2,1]size3,因此我们遍历 6 次(从 05)。

第 1 次迭代(i = 0)

  • 当前元素是 nums[0 % 3] = nums[0] = 1
  • 栈为空,因此我们将 0 压入栈
  • 栈状态:[0]

第 2 次迭代(i = 1)

  • 当前元素是 nums[1 % 3] = nums[1] = 2
  • 栈不为空,且 2 > nums[st.peek()] = nums[0] = 1
    • 更新 result[0] = 2 (栈顶元素的下一个更大元素是 2
    • 弹出栈顶元素 0
  • 将 1 压入栈
  • 栈状态:[1]
  • 结果数组:[2, -1, -1]

第 3 次迭代(i = 2)

  • 当前元素是 nums[2 % 3] = nums[2] = 1
  • 栈不为空,且 1 不大于 nums[st.peek()] = nums[1] = 2
  • 将 2 压入栈
  • 栈状态:[1, 2]
  • 结果数组:[2, -1, -1]

第 4 次迭代(i = 3)

  • 当前元素是 nums[3 % 3] = nums[0] = 1
  • 栈不为空,且 1 不大于 nums[st.peek()] = nums[2] = 1
  • 将 3 % 3 = 0 压入栈
  • 栈状态:[1, 2, 0]
  • 结果数组:[2, -1, -1]

第 5 次迭代(i = 4)

  • 当前元素是 nums[4 % 3] = nums[1] = 2
  • 栈不为空,且 2 > nums[st.peek()] = nums[0] = 1
    • 更新 result[0] = 2 (栈顶元素的下一个更大元素是 2
    • 弹出栈顶元素 0
  • 栈不为空,且 2 > nums[st.peek()] = nums[2] = 1
    • 更新 result[2] = 2 (栈顶元素的下一个更大元素是 2
    • 弹出栈顶元素 2
  • 将 4 % 3 = 1 压入栈
  • 栈状态:[1]
  • 结果数组:[2, -1, 2]

第 6 次迭代(i = 5)

  • 当前元素是 nums[5 % 3] = nums[2] = 1
  • 栈不为空,且 1 不大于 nums[st.peek()] = nums[1] = 2
  • 将 5 % 3 = 2 压入栈
  • 栈状态:[1, 2]
  • 结果数组:[2, -1, 2]

总结

最终得到的结果数组是 [2, -1, 2],每个位置对应的下一个更大元素为:

  • 对于 nums[0] = 1,下一个更大的元素是 2
  • 对于 nums[1] = 2,在循环数组中没有更大的元素,因此结果是 -1
  • 对于 nums[2] = 1,下一个更大的元素是 2

三、柱状图中最大的矩形 

题目:

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

示例 1:

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]
输出: 4

思路:

与接雨水类似,同时处理两边的元素,需要注意的是,题目中为了边界的处理需要对数组进行扩容操作

代码:

public int largestRectangleArea(int[] heights) {
    Stack<Integer> st = new Stack<Integer>(); // 用于存储直方图的柱子下标

    // 数组扩容,在头和尾各加入一个高度为0的元素
    int[] newHeights = new int[heights.length + 2];
    newHeights[0] = 0; // 头部填充0
    newHeights[newHeights.length - 1] = 0; // 尾部填充0
    for (int index = 0; index < heights.length; index++) {
        newHeights[index + 1] = heights[index]; // 复制原数组到扩容后的数组
    }

    heights = newHeights; // 更新原数组为扩容后的数组

    st.push(0); // 初始将第一个填充的0的下标压入栈
    int result = 0; // 记录最大的矩形面积

    // 从第二个元素开始遍历(实际的第一个元素是新数组中的1)
    for (int i = 1; i < heights.length; i++) {
        // 比较当前柱子的高度与栈顶柱子的高度
        if (heights[i] > heights[st.peek()]) {
            st.push(i); // 当前柱子比栈顶柱子高,直接压栈
        } else if (heights[i] == heights[st.peek()]) {
            st.pop(); // 当前柱子与栈顶柱子高度相等,弹出栈顶,重新压栈
            st.push(i);
        } else {
            // 当前柱子低于栈顶柱子,弹出栈顶柱子,计算以弹出的柱子为高度的矩形面积
            while (heights[i] < heights[st.peek()]) {
                int mid = st.peek(); // 弹出的柱子下标
                st.pop();
                int left = st.peek(); // 弹出柱子左边最近的柱子下标
                int right = i; // 当前柱子下标
                int w = right - left - 1; // 宽度为右下标 - 左下标 - 1
                int h = heights[mid]; // 高度为弹出的柱子的高度
                result = Math.max(result, w * h); // 更新最大矩形面积
            }
            st.push(i); // 压入当前柱子的下标
        }
    }
    return result; // 返回最大矩形面积
}
  1. 数组扩容:在原数组的两端各加一个高度为0的柱子,简化处理边界情况。
  2. 栈的作用:用栈来保存柱子的下标,保证栈中柱子的高度是递增的。
  3. 面积计算:当发现当前柱子高度小于栈顶柱子的高度时,弹出栈顶柱子,并计算以该柱子为高度的矩形面积。
  4. 更新最大面积:每次计算出的矩形面积都与当前最大值比较,保持最大面积。

 

栈中元素的动态分析

以例子

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10
  1. 初始化

    • 扩容后的数组:[0, 2, 1, 5, 6, 2, 3, 0]
    • 栈:[0](索引0的0)
  2. 遍历开始

    • i=1heights[1] = 2

      • 2 > 0(栈顶0),压栈。
      • 栈:[0, 1]
    • i=2heights[2] = 1

      • 1 < 2(栈顶1),弹出栈顶1,计算面积。
        • 高度:2
        • 左边界:0,右边界:2
        • 宽度:2 - 0 - 1 = 1
        • 面积:2 * 1 = 2
      • 继续弹出,0的矩形高度为0,面积为0,不更新结果。
      • 1压栈。
      • 栈:[0, 2]
    • i=3heights[3] = 5

      • 5 > 1(栈顶2),压栈。
      • 栈:[0, 2, 3]
    • i=4heights[4] = 6

      • 6 > 5(栈顶3),压栈。
      • 栈:[0, 2, 3, 4]
    • i=5heights[5] = 2

      • 2 < 6(栈顶4),弹出栈顶4,计算面积。
        • 高度:6
        • 左边界:3,右边界:5
        • 宽度:5 - 3 - 1 = 1
        • 面积:6 * 1 = 6
      • 2 < 5(栈顶3),弹出栈顶3,计算面积。
        • 高度:5
        • 左边界:2,右边界:5
        • 宽度:5 - 2 - 1 = 2
        • 面积:5 * 2 = 10
      • 继续弹出,2的矩形高度为1,面积1 * 1 = 1,不更新结果。
      • 2压栈。
      • 栈:[0, 2, 5]
    • i=6heights[6] = 3

      • 3 > 2(栈顶5),压栈。
      • 栈:[0, 2, 5, 6]
    • i=7heights[7] = 0

      • 0 < 3(栈顶6),弹出栈顶6,计算面积。
        • 高度:3
        • 左边界:5,右边界:7
        • 宽度:7 - 5 - 1 = 1
        • 面积:3 * 1 = 3
      • 0 < 2(栈顶5),弹出栈顶5,计算面积。
        • 高度:2
        • 左边界:2,右边界:7
        • 宽度:7 - 2 - 1 = 4
        • 面积:2 * 4 = 8
      • 继续弹出,0的矩形高度为0,面积为0,不更新结果。
      • 0压栈。
      • 栈:[7]
  3. 最终结果:最大矩形面积为10。

总结:栈中的元素是柱子的索引,保持柱子的高度递增。每次遇到较小的柱子时,弹出栈顶,计算相应的矩形面积,并更新最大面积。

今天的学习就到这里 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2069227.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Oracle问题】ORA-12516:监听程序找不到符合协议堆栈要求的可用处理程序问题处理

ORA-12516:监听程序找不到符合协议堆栈要求的可用处理程序 这种一般都是会话数或者process和session已经到达临界值或者超过最大值了。 1.先cmd登录sqlplus&#xff1a; sqlplus / as sysdba;2.查看设置的最大的连接数和进程数&#xff1a; show parameter process&#xff…

三维平面电磁铁、交流电磁铁、显微镜磁场北京大学方案

根据用户北京大学需求设计制造方案如下 三维平面电磁铁产品规格 5MPS63-25型三维平面电磁铁&#xff0c;X、Y方向磁场由2对正交的磁极产生&#xff0c;Z轴由一组同轴线圈产生&#xff1b; 每轴对应的两个线圈正接产生均匀磁场&#xff0c;反接产生梯度磁场&#xff1b; …

JetBrains GoLand 2024.2 (macOS, Linux, Windows) - 为 Go 开发者打造的完整 IDE

JetBrains GoLand 2024.2 (macOS, Linux, Windows) - 为 Go 开发者打造的完整 IDE JetBrains 跨平台开发者工具 请访问原文链接&#xff1a;https://sysin.org/blog/jetbrains-goland/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sy…

一文解决---IDEA汉化问题(含中英文切换)

一、英文->中文&#xff1a; ①.下载汉化包插件&#xff1a; 操作顺序&#xff1a;File->Settings->Plugins 在搜索框输入Chinese&#xff0c;然后找到 Chinese (Simplified) Language &#xff08;汉化插件&#xff09;&#xff0c;等待下载完→Install (安装)&…

《机器学习》—— 通过下采样方法实现逻辑回归分类问题

文章目录 一、什么是下采样方法&#xff1f;二、通过下采样方法实现逻辑回归分类问题三、下采样的优缺点 一、什么是下采样方法&#xff1f; 机器学习中的下采样&#xff08;Undersampling&#xff09;方法是一种处理不平衡数据集的有效手段&#xff0c;特别是在数据集中某些类…

VTK随笔三:坐标系统、空间变换、VTK管线、VTK智能指针

一、坐标系统 Model坐标系统&#xff1a;定义模型时所采用的坐标系统&#xff0c;通常是局部的笛卡尔坐标系。World坐标系统&#xff1a;是放置Actor的三维空间坐标系&#xff0c;Actor(vtkActor类)其中的一个功能就是负责将模型从Model坐标系统变换到World坐标系统。每一个模…

python动画:实现贝塞尔曲线【bezier】

贝塞尔曲线在计算机图形学中用于绘制形状、用于 CSS 动画和许多其他地方。它们是一件非常简单的事情&#xff0c;值得学习一次&#xff0c;然后在矢量图形和高级动画的世界中感到舒适。 一.控制点 贝塞尔曲线由控制点可能有 2、3、4 或更多。 例如&#xff0c;二次贝塞尔曲线…

基于java教师课堂教学质量评价系统设计与实现

课题背景 高等学校的根本任务是培养人才&#xff0c;课堂教学是高校完成人才培养的重要环节&#xff0c;因此&#xff0c; 教师教学质量的高低对学生掌握和运用知识的程度有着密不可分的作用&#xff0c;为了保证教师的教学质量&#xff0c;教学评价成为了各高校衡量教师教学质…

大数据-92 Spark 集群 SparkRDD 原理 Standalone详解 ShuffleV1V2详解 RDD编程优化

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

盘古信息IMS MCM制造协同管理系统:为中小企业数字化转型量身打造的数字化方案

近年来&#xff0c;全球经济的不稳定性&#xff0c;给中小企业的经营和发展带来了巨大的挑战。为提升企业竞争力&#xff0c;中小企业纷纷谋求数字化转型路径&#xff0c;优化生产流程、提高运营效率、降低生产成本&#xff0c;以应对变幻莫测的市场环境。IMS MCM是盘古信息为广…

go中 panicrecoverdefer机制

go的defer机制-CSDN博客 常见panic场景 数组或切片越界&#xff0c;例如 s : make([]int, 3); fmt.Println(s[5]) 会引发 panic: runtime error: index out of range空指针调用&#xff0c;例如 var p *Person; fmt.Println(p.Name) 会引发 panic: runtime error: invalid m…

IT 人员配置的演变趋势:2024 年上半年的见解

随着我们深入研究 2024 年的动态格局&#xff0c;IT 人员配置领域在前六个月见证了几个值得注意的趋势和变化。 这些发展不仅反映了企业不断变化的需求&#xff0c;也凸显了技术对劳动力动态的变革性影响。 以下是在这个关键年份上半年塑造 IT 人员配置的一些关键趋势的详细介…

Python办公自动化smtplib实现自动发送邮件

学好python自动化&#xff0c;走遍天下都不怕&#xff01;&#xff01; 今天主要学习如何利用python自动化分析处理数据并以附件形式发送邮箱。需要安装配置python的运行环境&#xff0c;以及电脑支持Excel文件&#xff0c;有可以正常使用的邮箱。还需要用到python的第三方模块…

剧本杀门店预约小程序,提高消费者体验

当下&#xff0c;剧本杀行业一直处于活跃的状态&#xff0c;不管是线下聚会还是休闲娱乐&#xff0c;剧本杀游戏都是消费者的选择&#xff0c;剧本杀市场具有非常大的发展前景。 剧本杀门店预约系统是一个高效、便捷的游戏预约方式&#xff0c;能够帮助消费者便捷预约&#xf…

基于ssm的爱心捐赠管理系统的设计与实现(论文+源码)_kaic

摘 要 在互联网高速发展的现在&#xff0c;许多的办公与应用从传统的实体办理变为了在线处理。网购与网淘商品的浏览也从大街小巷变为了在线预览&#xff0c;从而使网上用户更好地对物品产生客观、全面立体的认知&#xff0c;并且对物品的对比也更加的省时省力。 爱心捐赠管…

一个简单的springboot项目(有源码)

开发一个springboot项目 代码迭代整合工具 gitee建模意义程序处理方式开发功能的步骤web服务网络状态码 web应用的开发分层springboot的作用 springboot框架搭建框架中各组件作用框架的演变如何提取hive中的表结构创建springboot 工程的引导模版 要选择aliyun &#xff0c;否则…

java ssl使用自定义证书或忽略证书

1.证书错误 Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 2.生成客户端证书 openssl x509 -in <(openssl s_client -connect 192.168.11.19:8101 -prexit 2>/dev/null) -ou…

linux neo4j 切换知识图谱

neo4j 安装 linux neo4j的安装可以浏览这篇文章&#xff1a; ubuntu sudo apt-get install neo4j 配置安装与设置远程访问 引言 如果你是window用户&#xff0c;直接下载桌面版进行安装与使用即可&#xff1b; 我有一台linux的服务器&#xff0c;想部署在上面&#xff0c;不…

UE5.4内容示例(5)UI_CommonUI - 学习笔记

https://www.unrealengine.com/marketplace/zh-CN/product/content-examples 《内容示例》是学习UE5的基础示例&#xff0c;可以用此熟悉一遍UE5的功能 UI_CommonUI可以看这个视频学习&#xff0c;此插件处于Beta状态&#xff0c;应用UI游戏方面&#xff0c;支持手柄等多输入端…

打饭-GYM

打饭 我还不信了&#xff0c;手动模拟一遍 再来了好看一点的图 ji12345a[i]21341120x3f0x3f0x3f0x3f0x3f114220x3f0x3f0x3f0x3f103520x3f0x3f0x3f0x3f9min(332,42)4020x3f0x3f0x3f8min(332,35)min(332,40)3190x3f0x3f7min(262,33)332490x3f0x3f6min(172,33)3120x3f0x3f0x3f5min…