【Hello Algorithm】单调栈(未完待续)

单调栈解决的问题

我们单调栈的提出主要是为了解决这么一个问题

现在给我们一个数组 现在要求你建立一张表 这张表中能够查询到两个信息

这两个信息分别是

  • 当前数字左边小于该数字并且下标位置最相近的下标
  • 当前数字右边小于该数字并且下标位置最相近的下标

同理 大于也可以

原本我们暴力的方法是每次遍历到需要知道前后下标的位置我们都往前或者是往后遍历出我们需要的数据 这样子的时间复杂度就是N平方

而我们单调栈的方法能做到 N 的时间复杂度

整体思路(无重复值版)

单调栈解决上面问题得思路如下

我们首先要准备一个空的栈 我们要确保这个栈从栈底到栈顶的数字由小到大

在这里插入图片描述

假设我们现在有一个数组 【3 ,4 ,2,6,1,7,0】

  • 此时由于栈里面没有数据我们的元素3 直接入栈 到下一个元素4
  • 此时由于栈里面的元素3 小于我们的元素4 符合规则 所以说元素4直接入栈 到下一个元素2
  • 此时由于栈里面的元素4 大于要插入的元素2 不符合规则 所以说元素4要出栈
  • 而在元素4出栈的一瞬间我们也就得到了我们需要的一组数组
  • 小于元素4并且位于左侧的数是3 小于元素4并且位于右侧的数是2
  • 后面的数据以此类推 … …

原理解释

  • 为什么栈下面的元素就是距离当前元素左侧最近的且小于当前数字的数

因为我们栈是严格按照从小到大的顺序进行排列的 所以说下面的元素一定小于当面的元素 所以说小于当前数字证明完毕

如果说还有其他元素距离当前元素的左侧更近并且小于当前元素 那么这个元素一定会出现在栈中这两个元素的中间

  • 为什么还未入栈的元素就是距离当前元素右侧最近且小于当前数字的数

因为我们栈是严格按照从小到大的顺序进行排列的 所以说只有比当前元素小的元素才能让当前元素出栈

如果说还有其他元素是距离当前元素的右侧更近并且小于当前元素 那么这个元素一定提前让当前元素出栈

整体思路(有重复值版)

在这里插入图片描述
有重复值版本和无重复值版本最主要的一个区别就是 有重复值版本里面存放的元素是一个链表 该链表连接着各个元素值相同的元素的下标

  • 寻找距离当前元素右侧最近并且小于该元素的值的元素步骤和无重复值一样
  • 寻找距离当前元素左侧最近并且小于该元素的值的元素时 如果下次是一个链表 则我们要选择这个链表的最后一个元素

代码表示(默认全部数字为正整数)

有重复值版

函数表示如下

vector<vector<int>> MonotonicStack(vector<int>& arr); 

返回值说明

我们返回的是一个二维数组

该二维数组的下标对应着数组中数字的下标 二维数组中的每一行有两个元素 这两个元素分别代表着距离当前元素左边(右边)最近的且小于当前元素的值 如果不存在该值 则填入 -1

参数说明

一个一维数组

无重复版代码表示如下

vector<vector<int>> MonotonicStack(vector<int>& arr)
{
  int N = static_cast<int>(arr.size());    
  vector<vector<int>> ans(N , vector<int>(2 , -1));    
  stack<int> st;    
    
  for (int i = 0; i < N; i++)    
  {    
    while(!st.empty() && arr[st.top()] > arr[i])    
    {    
      int PopIndex = st.top();    
      st.pop();    
    
      int LeftIndex = st.empty() ? -1 : st.top();                                                                                               
    
      ans[PopIndex][0] = LeftIndex;    
      ans[PopIndex][1] = i;    
    }    
    
    st.push(i);    
  }    
    
  // 此时栈中还有剩余元素未清空  
      while (!st.empty())    
  {    
    int PopIndex = st.top();    
    st.pop();    
    
    int LeftIndex = st.empty()? -1 : st.top();    
    
    ans[PopIndex][0] = LeftIndex;    
    ans[PopIndex][1] = -1; // 这里也可以不写 因为本来就是-1
  }

  return ans;
}

有重复值版

函数表示如下

vector<vector<int>> MonotonicStack(vector<int>& arr); 

返回值说明

我们返回的是一个二维数组

该二维数组的下标对应着数组中数字的下标 二维数组中的每一行有两个元素 这两个元素分别代表着距离当前元素左边(右边)最近的且小于当前元素的值 如果不存在该值 则填入 -1

参数说明

一个一维数组

有重复值版代码表示如下

vector<vector<int>> MonotonicStack(vector<int>& arr)    
{    
  int N = static_cast<int>(arr.size());    
  vector<vector<int>> ans(N , vector<int>(2 , -1));    
                                                                                                                                  
  stack<list<int>> st;    
    
  for (int i = 0; i < N ; i++)    
  {    
    while(!st.empty() && arr[st.top().front()] > arr[i])    
    {    
      list<int> popIs = st.top();    
      st.pop();    
    
      int LeftIndex = st.empty() ? -1 : st.top().back();    
      // 之后依次更新该链表中的所有位置     
      for (auto x : popIs)    
      {    
        ans[x][0] = LeftIndex;    
        ans[x][1] = i;    
      }    
    }    
    
    if (!st.empty() && arr[st.top().front()] == arr[i])    
    {    
      st.top().push_back(i);    
    }
        else // empty ||  <       
    {
      list<int> Ins = {i};
      st.push(Ins);
    }                                                                                                                             
  }

  // 更新完毕之后可能栈里面还有值 也要全部更新完毕 最后返回ans
  while(!st.empty())
  {
    list<int> popIs = st.top();
    st.pop();

    int LeftIndex = st.empty() ? -1 : st.top().back();
    // 之后依次更新该链表中的所有位置 
    for (auto x : popIs)
    {
      ans[x][0] = LeftIndex;
      ans[x][1] = -1;
    }
  }

  return ans;
}

单调栈相关题目

题目一

给定一个只包含正数的数组arr , 该数组中的任意一个子数组sub 都会有一个A指标

A指标的计算方法为 (sub数组的和)* (sub数组中的最小值)

现在要求你设计一个函数 返回arr中所有子数组中最大的A指标

注意 子数组是连续的

解题思路

解决这个问题的思路其实很简单 假设我们目前有一个数组 arr 其中A指标最大的子数组是sub

那么sub中的最小值是不是肯定是数组arr的一个数字

既然我们确定了最小值 A指标的计算公式又是 最小值 * (sub数组和)

那么我们是不是只要让以该下标作为最小值的子数组尽可能的大就好了啊

那么现在的问题就转化为了 怎么才能让这个子数组尽可能的大呢?

一个比较笨的办法就是依次往左往右遍历嘛 但是这样子时间复杂度就会很高

还有一种方式就是按照我们写的单调栈 只需要将ans数组中的下标加一减一就好了(边界问题需要自己考虑)

关于数组和的计算我们可以使用前缀和数组来简化

代码表示如下

  vector<int> arr = {3 , 1, 1 ,3 , 5, 5, 5};

  vector<vector<int>> ans = MonotonicStack(arr);
  int N = static_cast<int>(arr.size());
  //  前缀和数组
  vector<int> prearr(N , 0);
  prearr[0] = arr[0] ;
  for(int i = 1; i < N; i++)    
  {    
    prearr[i] = prearr[i - 1] + arr[i];    
  }    
    
  vector<int> max_ans(N , 0);    
  // 以数组的每个位置作为最小值 尽可能扩大数组范围 计算出结果 最后结果就在其中                 
  for (int i = 0; i < N; i++)    
  {    
    int LeftIndex = ans[i][0] == -1 ? 0  : ans[i][0] + 1;
    int RightIndex = ans[i][1] == -1 ? N - 1 : ans[i][1] - 1;
       int sub_sum = prearr[RightIndex] - prearr[LeftIndex] + arr[LeftIndex];

    max_ans[i] = sub_sum * arr[i];                                                             
  }

之后max_ans数组里面的结果就是最终答案

题目二

给定一个非负数组 arr 代表直方图 返回直方图的最大长方形面积

题目解释

直方图的含义如下 以数组 【3 , 2,4 ,2 , 1】为例 如下图

在这里插入图片描述

每个下标代表着当前位置的告诉 最终他们组成了一个图形

现在要你求这个图形中 最大的长方形面积

解题思路

其实这个问题和上个问题的思路完全一致

我们只需要遍历数组中每一个元素 然后尽可能的让这个长方形变大即可

代码类似于题目一 这里就不提供了

题目三

给定一个二维数组 matrix 其中的值不是0就是1

返回由1组成的一个最大子矩阵 给出其中1的数量

题目解释

给出一个二维数组图 matrix

在这里插入图片描述

我们的子矩阵中只能包含1 现在要求你在图中找出一个最大的子矩阵 并且返回1的个数

解题思路

如果我们使用纯暴力的方法去解这道题的话 它的时间复杂度就是N的四次方

(计算方式为 我们在二维表中任意选一点的时间复杂度为n平方 而两点可以组成一个矩阵 所以说时间复杂度为n的四次方)

我们正常的解题思路如下

既然要求的是在matrix中的一个子矩阵 那么该矩阵的 “地基”是不是肯定是matrix中的某一行啊

那么只要我们依次算出以matrix中每一行作为地基时最大的矩阵即可

代码思路

我们以matrix的列数为大小创建一个数组

该数组中更新以每行作为地基时的直方图 以下图为例 更新的直方图数组为

在这里插入图片描述

1 1 1 0 1
0 2 2 1 0
1 3 3 2 1
2 4 0 0 2
0 0 1 1 3
1 1 0 0 4
2 2 0 0 5

接下来的问题就转化为了计算上面这些直方图中矩形面积的最大值 类似题目二

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习入门案例(3)之使用决策树预测是否适合打网球

大家好&#xff0c;我是邵奈一&#xff0c;一个不务正业的程序猿、正儿八经的斜杠青年。 1、世人称我为&#xff1a;被代码耽误的诗人、没天赋的书法家、五音不全的歌手、专业跑龙套演员、不合格的运动员… 2、这几年&#xff0c;我整理了很多IT技术相关的教程给大家&#xff0…

如何解决网页中的pdf文件无法下载?pdf打印显示空白怎么办?

问题描述 偶然间&#xff0c;遇到这样一个问题&#xff0c;一个网页上的附件pdf想要下载打印下来&#xff0c;奈何尝试多种办法都不能将其下载下载&#xff0c;点击打印出现的也是一片空白 百度搜索了一些解决方案都不太行&#xff0c;主要解决方案如&#xff1a;https://zh…

【万字长文】Python 日志记录器logging 百科全书 之 日志过滤

Python 日志记录器logging 百科全书 之 日志过滤 前言 在Python的logging模块中&#xff0c;日志过滤器&#xff08;Filter&#xff09;用于提供更细粒度的日志控制。通过过滤器&#xff0c;我们可以决定哪些日志记录应该被输出&#xff0c;哪些应该被忽略。这对于复杂的应用…

【每日一题】—— D. Epic Transformation(Codeforces Round 710 (Div. 3))(找规律+贪心)

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

vue离线地图(瓦片)

最近公司要弄一个这样的离线地图&#xff0c;要求在图上打点画线之类的。折腾了几天&#xff0c;学习了三种方式&#xff1a; 1.拿到各省市区的经纬度json&#xff0c;通过echarts来制作&#xff0c;再套一个卫星图的地图背景 2.下载地图瓦片&#xff0c;再通过百度/高德的离线…

image J 对Western blot 条带进行灰度分析 量化分析

用ImageJ对条带进行定量分析 | Public Library of Bioinformatics (plob.org) 3分钟Get&#xff01;大牛教你用 image J 对Western blot 条带进行灰度分析&#xff01; - 哔哩哔哩 (bilibili.com) 科研人员做的western blot实验一般需要对其结果扫描后进行灰度分析&#xff0…

【Qt之QWizard】使用2,示例分析

效果图 根据首页的选择不同&#xff0c;进入不同的选项。 以下是代码。 示例 .h #ifndef LICENSEWIZARD_H #define LICENSEWIZARD_H#include <QWizard>QT_BEGIN_NAMESPACE class QCheckBox; class QLabel; class QLineEdit; class QRadioButton; QT_END_NAMESPACEcla…

vue请求代理查看真实地址

查看真实地址方式&#xff1a; 通过配置vue.config.js文件&#xff0c;直接在请求头输出完整地址&#xff1a; /api/: { changeOrigin: true, target: process.env.VUE_APP_PLATFORM_URL, logLevel: debug, // 在终端输出 onProxyRes(proxyR…

请求头,响应头

目录 常见的请求方式 GET/POST HEAD&#xff08;报文首部&#xff0c;验证URI有效性&#xff09; PUT/DELETE(报文文件) OPTIONS&#xff08;查询URI支持的HTTP方法&#xff09; Connection: keep-alive TCP 就会一直保持连接。 Cache-Control public&#xff1a;响应…

数据银行:安全保障的重要一环

随着信息技术的快速发展&#xff0c;数据银行已经成为了我们日常生活中不可或缺的一部分。它存储了我们的个人信息、财务数据、医疗记录等重要信息&#xff0c;这些信息对于我们的生活和工作至关重要。然而&#xff0c;由于数据的安全性备受关注&#xff0c;因此&#xff0c;对…

【星海出品】SDN neutron (四) 流分析

Neutron框架之流分析 1.控制端neutron-server通过wsgi接收北向REST API请求&#xff0c;neutron-plugin通过rpc与设备端进行南向通信。 2.设备端agent则向上通过rpc与控制端进行通信&#xff0c;向下则直接在本地对网络设备进行配置。 3.Neutron-agent的实现很多&#xff0c;彼…

容斥dp,二项式反演

前言 由于水平有限&#xff0c;这篇文章比较难懂&#xff0c;并且也有很多不够透彻的地方&#xff0c;如果您有任何的看法&#xff0c;非常感谢您私信指导。 容斥dp 用dp的方法来描述容斥&#xff0c;大概的想法是&#xff0c;把容斥系数分到每一步里去乘。 通常当你有容斥…

本田发布全新CB1000 Hornet,是杜卡迪街霸劈了腿还是Z1000红杏出墙?

米兰车展上&#xff0c;本田带来了全新的大黄蜂CB1000 Hornet&#xff0c;外观方面抛弃了之前的本田推出的Neo Sports Caf风格&#xff0c;新款的外观看起来要更加战斗一点。不过新的这个前脸改的&#xff0c;我只能说是杜卡迪街霸劈了腿还是Z1000红杏出墙&#xff1f;外观方面…

【Python】Numpy(学习笔记)

一、Numpy概述 1、Numpy Numpy&#xff08;Numerical Python&#xff09;是一个开源的Python科学计算库&#xff0c;用于快速处理任意维度的数组。 Numpy使用ndarray对象来处理多维数组&#xff0c;该对象是一个快速而灵活的大数据容器&#xff0c; Numpy num - numerical 数…

Kohana框架的安装及部署

Kohana框架的安装及部署 tipsKohana安装以及部署1、重要文件作用说明1.1 /index.php1.2 /application/bootstrap.php 2、项目结构3、路由配置3.1、隐藏项目入口的路由3.2、配置默认路由3.3、配置自定义的路由(Controller目录下的控制器)3.4、配置自定义的路由(Controller/direc…

JS操作canvas

<canvas>元素本身并不可见&#xff0c;它只是创建了一个绘图表面并向客户端js暴露了强大的绘图API。 1 <canvas> 与图形 为优化图片质量&#xff0c;不要在HTML中使用width和height属性设置画布的屏幕大小。而要使用CSS的样式属性width和height来设置画布在屏幕…

父组件用ref获取子组件数据

子组件 Son/index.vue 子组件的数据和方法一定要记得用defineExpose暴露&#xff0c;不然父组件用ref是获取不到的&#xff01;&#xff01;&#xff01; <script setup> import { ref } from "vue"; const sonNum ref(1); const changeSon () > {sonNum.…

DAY54 392.判断子序列 + 115.不同的子序列

392.判断子序列 题目要求&#xff1a;给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;例如&#xff0c;"ace"是…

探秘Vue组件间通信:详解各种方式助你实现目标轻松搞定!

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一…

threejs(13)-着色器设置点材质

着色器材质内置变量 three.js着色器的内置变量&#xff0c;分别是 gl_PointSize&#xff1a;在点渲染模式中&#xff0c;控制方形点区域渲染像素大小&#xff08;注意这里是像素大小&#xff0c;而不是three.js单位&#xff0c;因此在移动相机是&#xff0c;所看到该点在屏幕…