【Hello Algorithm】滑动窗口内最大值最小值

news2024/9/22 0:58:07

滑动窗口介绍

滑动窗口是一种我们想象中的数据结构 它是用来解决算法问题的

在这里插入图片描述

我们可以想象出一个数组 然后再在这个数组的起始位置想象出两个指针 L 和 R

在这里插入图片描述

我们对于这两个指针做出以下规定

  • L 和 R指针只能往右移动
  • L指针不能走到R指针的右边
  • 我们只能看到L指针和R指针中间的数字

比如说当前L和R指针重合 我们就什么数字都看不见

如果此时R指针往右走一步 那么我们就能看到L指针和R指针中间的数组的数字

又因为这种移动的方式特别像滑动 所以说我们将这种想象出来的数据结构叫做滑动窗口

如何确定一个滑动窗口内的最大值

如果我们每次都遍历去获取滑动窗口的最大值的话那么每次获取的时间复杂度就是O(N)了 这样子明显很复杂 所以说我们需要想想别的方式去找到最大值

这里直接给出结论:

  • 我们使用双端队列去获取一个滑动窗口内的最大值
  • 双端队列的意义是 : 此时开始缩小滑动窗口 哪些数字可能成为最大值

为了让双端队列能够实现它的意义 我们做出以下规定

  • 让R指针向右滑动的时候 我们从右边插入数字的下标到双端队列中
  • 如果说插入的数字要大于原来的数字 我们让原来的数字出队列
  • 让L指针向右滑动的时候 我们从左边开始比较双端队列第一个(左边数起)下标和L指针的下标
  • 如果说L指针的下标要大于双端队列最左边的下标 则将其弹出

至于C++中的双端队列 大家可以参考这篇博客 双端队列

窗口内最大值

假设一个固定大小为W的窗口 依次划过数组arr

让你依次返回每次窗口移动时窗口中的最大值

假设数组arr为 【4 , 3 , 5 ,4, 3, 3, 6, 7】 W = 3

返回【5 , 5, 5, 4, 6, 7】


这道题目实际上就是一个滑动窗口最大值的简单版本

我们使用一个双端队列就能很轻松的实现

面对这个问题我们可以拆分成两部分来解决

  1. 首先把滑动窗口的大小扩大到3
  2. 接着滑动窗口整体开始移动

两部分的代码都不算难 代码表示如下

void process5(vector<int>& arr , vector<int>& ans , int W)
{
  deque<int> dq(0  , 0);

  for (int R = 0; R < W; R++)
  {
    while (!dq.empty() && arr[dq.front()] <= arr[R])
    {
      dq.pop_back();
    }    
    
    dq.push_back(R);    
  }    
  int R = W - 1;    
  int L = 0;    
  int N = static_cast<int>(arr.size());    
  while (R < N)    
  {    
    while (!dq.empty() && arr[dq.back()] <= arr[R])                                                                                                                          { 
        dq.pop_back();
    }                     
         
    dq.push_back(R);
                                                                                                                                                                     
    while (L > dq.front())    
    {                         
       dq.pop_front();
    }                     
         
    ans.push_back(arr[dq.front()]);
    L++;    
    R++;
  }
}

这道题目给我们的提醒主要有两个

  1. 当我们R指针右移的时候要使用while来进行判断
  2. R指针在初始化之后要重置 否则会出现一些错误 为了避免这些错误 我们最好每次使用一个变量之前对其进行初始化

子数组中符合条件的个数

给定一个整形数组arr 和一个整数num

某个arr中的子数组sub 必须要满足

sub中的最大值减去sub中的最小值小于等于num

返回sub中达标的子数组的数量


我们首先分析下问题

如果我们没有学过滑动窗口和双端队列 那么这道题我们会使用暴力的方式来得到答案

三个for循环嵌套 时间复杂度就变成了n的三次方 这显然是不可以的

当我们使用滑动窗口解决这个问题的时候 由于我们的左右两个下标都是单向的往右边移动 所以说此时的时间复杂度就是N


当我们得到两个下标满足上述条件时 比如说0 ~ N上的最大值减去0~N中的最小值小于等于num

此时我们就可以说 左下标为0 右下标为0 ~ N-1上的任意一个子数组都满足条件

因为此时最大值只有可能变小 最小值只有可能变大 所以说它们之间的差值肯定还是会小于等于num

所以我们就能确定 以0为左边界 N为右边界上符合条件的子数组有 N - 0 + 1个

之后我们左边界右移即可


整体思路如下

  • 我们使用两个双端队列来记录子数组的最大值和最小值
  • 我们让右边界一直右移动 直到子数组不满足条件为止
  • 此时通过上面的技巧计算出左边界到右边界-1的满足条件子数组个数 之后左边界++

代码表示如下

int process(vector<int>& arr, int num)
{
    deque<int> Max_Win(0, 0);
    deque<int> Min_Win(0, 0);

    int count = 0;
    int R = 0;
    int N = static_cast<int>(arr.size());
    for (int L = 0; L < N; L++)
    {
        while (R < N)
        {
            while (!Max_Win.empty() && arr[Max_Win.back()] <= arr[R])
            {
                Max_Win.pop_back();
            }
            Max_Win.push_back(R);

            while (!Min_Win.empty() && arr[Min_Win.back()] >= arr[R])
            {
                Min_Win.pop_back();
            }
            Min_Win.push_back(R);

            if (arr[Max_Win.front()] - arr[Min_Win.front()] > num)
            {
                break;
            }
            else
            {
                R++;
            }

        }

        count += R - L;

        if (Max_Win.front() == L)
        {
            Max_Win.pop_front();
        }

        if (Min_Win.front() == L)
        {
            Min_Win.pop_front();
        }
    }

    return count;
}

这里有一点需要注意的是

 count += R - L;

语句必须要放到while循环的外面才行 否则会因为R下标越界的问题而导致不会执行if语句 最终导致count计算不完整

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

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

相关文章

【面向对象程序设计】Java大作业 汽车租赁管理系统V4.0

前言 自己大二时候使用JavaMysql写的租车系统大作业V4.0黑窗口版的一个记录&#xff0c;简简单单的黑窗口&#xff0c;不是炫酷的前后端分离也没用GUI&#xff0c;但功能完善&#xff0c;该有都有&#xff0c;当时得分也还是挺不错的 技术栈 Java (jdk8)Mysql 资源包内容 …

8、电路综合-基于简化实频的SRFT微带线的带通滤波器设计

8、电路综合-基于简化实频的SRFT微带线的带通滤波器设计 此处介绍微带线综合的巴特沃斯带通滤波器和切比雪夫带通滤波器的设计方法。对于理查德域的网络综合技术而言&#xff0c;这种带通综合和低通综合在本质上并无区别&#xff0c;因为理查德域函数是周期的。低通滤波器的SR…

一文讲明:企业知识库的作用和搭建方法

在现代商务环境中&#xff0c;企业面临着大量的信息和知识流动。这些信息和知识散落在各个部门、团队甚至个人之间&#xff0c;难以进行有效的整合和利用。而企业知识库的出现解决了这一问题。它提供了一个统一的平台&#xff0c;将分散的信息汇聚到一个集中的数据库中&#xf…

jenkins如何安装?

docker pull jenkins/jenkins:lts-centos7-jdk8 2.docker-compose.yml version: 3 services:jenkins:image: jenkins/jenkins:lts-centos7-jdk8container_name: my-jenkinsports:- "8080:8080" # 映射 Jenkins Web 界面端口volumes:- jenkins_home:/var/jenkins_h…

657. 机器人能否返回原点

657. 机器人能否返回原点 Java代码&#xff1a; class Solution {public boolean judgeCircle(String moves) {int[] x {0, 0, -1, 1};int[] y {1, -1, 0, 0};String str "UDLR";int xx 0, yy 0;for (int i 0; i < moves.length(); i) {xx x[str.indexOf(…

小美的修路(最小生成树练习)

本题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 题目&#xff1a; 样例&#xff1a; 输入 3 4 1 2 3 1 1 2 2 0 1 3 1 0 2 3 3 0 输出 2 1 3 思路&#xff1a; 由题意&#xff0c;这里建造的城市需要修路&#xff0c;且每个城市之间可以联通&#xff0c;且 是 1 …

网络协议--TCP的成块数据流

20.1 引言 在第15章我们看到TFTP使用了停止等待协议。数据发送方在发送下一个数据块之前需要等待接收对已发送数据的确认。本章我们将介绍TCP所使用的被称为滑动窗口协议的另一种形式的流量控制方法。该协议允许发送方在停止并等待确认前可以连续发送多个分组。由于发送方不必…

安卓APP抓包环境配置

软件下载安装 所需软件 夜神模拟器 Fiddler platform-tools 下载地址 https://wwb.lanzoum.com/b01xhmk5e 密码:hv99 配置Fiddler 由于fiddler只默认抓取HTTP的请求&#xff0c;若想抓取HTTPS的请求&#xff0c;则需要设置HTTPS的各项值 设置HTTPS各项值 在fiddler菜单项…

思维模型 棘轮效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。由俭入奢易&#xff0c;由奢入俭难&#xff0c;到底因为啥&#xff1f;棘轮效应。 1 棘轮效应的应用 1.1 恋爱中的棘轮效应 小美是一个漂亮的女孩&#xff0c;家庭条件也不错&#xff0c;…

R2R 的一些小tip

批次间控制器(Run-to-run Controller)&#xff0c;以应对高混合生产的挑战。将最优配方参数与各种工业特征相关联的模型是根据历史数据离线训练的。预测的最优配方参数在线用于调整工艺条件。 批次控制(R2R control)是一种先进的工艺控制技术&#xff0c;可在运行(如批次或晶圆…

网络滤波器/网络滤波器/脉冲变压器要怎样进行测试,一般要测试哪些参数?

Hqst华强盛导读&#xff1a;网络滤波器/网络滤波器/脉冲变压器要怎样进行测试&#xff0c;一般要测试哪些参数&#xff1f;测试网络滤波器的测试方法和步骤如何&#xff0c;需用到哪些测试工具和仪器设备呢&#xff1f; 一&#xff0c;网络流量的监控和过滤能力测试&am…

教你自己动手搭建一个传奇游戏,自己和自己玩,找找当年的感觉

传奇游戏承载了一代人的青春记忆。在那个年代&#xff0c;很多人都会在网吧里玩传奇游戏&#xff0c;与朋友一起组队打怪、刷装备。这些经历不仅让很多8090终生难忘&#xff0c;也成为了我们青春岁月中最珍贵的回忆。 虽然现在的传奇游戏已经逐渐淡出了人们的视线&#xff0c;…

C++: 类和对象(上)

文章目录 1. 面向对象和面向对象初步认识2. 类的引入3. 类的访问限定符4. 类的定义类的两种定义方式成员变量名规则的建议 5. 类的作用域6. 类的实例化7. 类对象模型计算类对象的大小 类的实际存储方式 8. this指针this指针的引入this指针的特性 1. 面向对象和面向对象初步认识…

c++指针【1】

在C中&#xff0c;指针是一种特殊的变量&#xff0c;它存储了一个内存地址。C指针在处理内存、数组、函数参数传递、文件I/O、动态内存分配等方面有着重要的应用。 一个指针变量通常被声明为特定类型的指针。例如&#xff0c;一个整数类型的指针可以指向一个整数。在声明指针变…

【软考】13. 结构化开发方法

《系统分析与设计概述》 当前系统的物理模型 ——> 当前系统的逻辑模型 ——> 目标系统的逻辑模型 ——> 目标系统的物理模型系统开发的目的&#xff1a;当前系统的物理模型 ——> 目标系统的物理模型 系统设计基本原理 抽象、模块化&#xff08;逐步分解&#xf…

「网络编程」数据链路层协议_ 以太网协议学习

「前言」文章内容是数据链路层以太网协议的讲解。 「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、以太网协议简介二、以太网帧格式&#xff08;报头&#xff09;三、MTU对上层协议的影响四、ARP协议4.1 ARP协议的作用4.2 ARP协议报头 一、以太网协…

【Spring】IOC快速入门

文章目录 1. Spring简介2. IOC快速入门 1. Spring简介 Spring是一个开放源代码的Java SE/EE一站式轻量级开源框架&#xff0c;由Rod Johnson发起并创立。其核心是IOC&#xff08;控制反转&#xff09;和AOP&#xff08;面向切面编程&#xff09;&#xff0c;使得开发者可以将对…

『 C++类与对象』继承

文章目录 继承的概念继承方式与访问限定符基类和派生类对象赋值转换继承中的作用域隐藏 派生类的默认成员函数构造函数拷贝构造函数赋值运算符重载析构函数 继承与友元静态成员与继承关系 继承的概念 继承的机制为,允许在以该类为基础上对类进行扩展,增加功能; 通常原来也就是…

社区买菜系统 JAVA开源项目

目录 项目内容 项目获取 项目截图 项目内容 基于VueSpringBootMySQL的社区买菜系统&#xff0c;包含菜品分类模块、菜品档案模块、菜品订单模块、菜品收藏模块、收货地址模块&#xff0c;还包含系统自带的用户管理、部门管理、角色管理、菜单管理、日志管理、数据字典管理、…