【滑动窗口】篮里到底能装 “几个水果” 呢?
Problem: 904. 水果成篮
文章目录
- 题目分析
- 算法原理分析
- 暴力枚举 + 哈希表
- 滑动窗口优化
- 数组再度优化
- 复杂度
- Code
题目分析
首先我们来分析一下本题的思路
- 首先我们通过题目的描述来理解一下其要表达的含义,题目给到我们一个
fruit
数组,里面存放的是每棵树上水果的数量。当我们拿着两个篮子去采摘水果的时候,可以选择任意一颗树开始采摘 - 虽然篮子里面只能装一种水果,即两个篮子只能有两种水果,但是每种水果所装的个数是不受限制的,所以只要水果的种类不超过两种即可
- 我们再来仔细看一下示例,比如这个示例1,
{1, 2, 1}
指的就是第一棵树上有一个【1号水果】,第二棵树上有一个【2号水果】,第三棵树上有一个【1号水果】。所以若是我们从第一棵开始采摘的话,可以采摘到 2个1号水果和1个2号水果 - 看到示例2,第一棵树上有一个【0号水果】,第二棵树上有一个【1号水果】,第三棵树上有一个【2号水果】,第三棵树上也有一个【2号水果】。
- 那若是我们从第一棵树开始采摘的话,摘完【0号】与【1号】水果就必须停下来了
- 若是我们从从第二棵树开始采摘的话,可以摘到【1号】与【2号】水果,并且【2号】水果可以采摘到两个,总水果树有3个,因此我们从第二棵树开始采摘最好
- 相信在看了上面的这些描述之后,读者应该是很清楚了,再来举一个例子
{1, 1, 1, 1, 1, 1}
,对于这个来说的话我们可以知道所摘的水果种类为1个,数量为6个
💬 那么本题我们就转换为了:找出一个最长的子数组长度,子数组中不超过两种类型的水果
算法原理分析
接下去我们就来分析一下本题的算法原理
暴力枚举 + 哈希表
- 首先最先相当的一定是暴力枚举,从第一个数开始往后进行枚举,以此类推,找到符合条件且长度最长的那一个。将做遍历到的加入到哈希表中,在遍历的过程中逐步进行判断即可
- 对于暴力解法的代码这里就不展示了,读者可以自己尝试着去写写看
滑动窗口优化
- 我们主要还是来讲一讲有关如何使用【滑动窗口】去做优化的事情,那对于滑动窗口而言我们知道是基于『双指针』的,所以在这里我们会需要有一个
left
指针和一个right
指针,当这个right
指针在向后移动的时候,当其无法在继续后移的时候表示[left, right]
这段区间内的水果数量已经到达②了,此时我们要考虑去做【出窗口】的操作
- 那这个时候我们让
left
指针向右移动一格,那此时读者可以思考一下当前的kinds
会出现什么样的变化?
- 在下面我列举出了两种,一个是 kinds不变的情况,因为其在后移的过程中,所取消掉的那一个水果的种类可能在
[left, right]
这个区间中还存在着这个种类的水果 - 第二个则是 kinds变小的情况,很好理解,若是在
[left, right]
区间中不包含了去除掉的这种水果的话,说明种类就会减少
- 与之相对应的我们要给出
right
指针相应的变化情况,若是kinds
不做变化的话,right
也无需去变化,因为再去右移的话可能会增加水果的种类;若是kinds
变小的话,那么right
就可以右移了,此时水果的种类就可以增加上去
那接下去呢,就让我们来看看,算法的执行过程是怎样的
- 在这里,我们的哈希表中所存的数据种类有两个,一个是 当前的水果种类,另一个呢则是 这个水果所对应的数量。
- 所以当我们使用右指针
right
在进行遍历的时候,其所对应的个数就++,那当这个数量大于2时就开始出窗口,所对应的则是left
左指针所指向的水果个数,但是呢这不是随便减的,当其减到【0】的时候,就要考虑从这个哈希表中删除掉这个相对应的水果了。 - 最后当出完窗口后我们要做的就是去更新这个最长的结果
好,以上就是有关【滑动窗口】相关的算法原理分析,详情代码见【Code】
💬 那有同学问:为何不直接讲滑动窗口相关的算法呢,而是每次都要讲一下暴力的解法,这不是多此一举吗?
- 同学,你应该要明白,我们在笔试面试时做算法题的时候,不是一看到一道题的时候就会想到它到底需要使用什么算法与数据结构,一般在拿到一道题的时候我们一般都会先去考虑暴力的解法。在思考一段时间之后才会去思考其是都可以一些算法来进行解决,我们平常在刷题的过程中,重要的不是你马上就能想到这个算法,我们更加关注的是这个解题的过程,大家在练习算法题的时候一定要格外注意
数组再度优化
有读者一定会疑惑,已经使用【滑动窗口】去做了优化为什么还要再去优化呢?我们可以来看看滑动窗口的代码提交之后的结果
- 可以看到效率并不是很高,原因其实就在于我们在频繁地去出入窗口
💬 那怎么去做一个优化呢?
- 我们再来观察一下题目给到我们的提示,这个
fruit
数组最大的个数为 $ 10^5 $,那我们其实可以不用使用哈希表去存储,而是直接利用【数组】去进行存放,因为对于数组的每个元素来说其实就是一种映射,和哈希表其实是差不多的原理
- 我们可以选择直接将数组的大小定为这个大小,并且呢我们需要在循环中放一个
kinds
变量用于代替哈希表的个数统计
int hash[100001] = { 0 };
for(int left = 0, right = 0, kinds = 0; right < n; ++right)
- 那么当我们在碰到水果的个数减到0的时候,不去使用
erase
,而是直接kinds--
去控制当前窗口中的水果个数
if(hash[fruits[left]] == 0)// hash.erase(fruits[left]);kinds--;
- 那么当我们一进循环遍历的时候,也需要去做一个判断,若是当前遍历到的这个水果的个数为0的话,就将
kinds
的个数进行一个累加
// 一进来判断发现水果的种类为0的话,则水果种类增加一种
if(hash[fruits[right]] == 0) kinds++;
- 那么在最后当我们中途去判断这个水果个数的时候,只需要对这个
kinds
做出判断即可
fi(kinds > 2)
💬 具体代码可以参照【Code】部分,我们来看到提交之后的执行结果可以观察到性能确实得到了大幅度的提升
复杂度
接下去我们来分析一下时间复杂度
- 时间复杂度:
首先是对于时间复杂度而言,【滑动窗口】部分的代码, 我们使用
left
和right
双指针去遍历查找整个数组, 并且在查找的过程中遇到水果数量 > 2需要去做出窗口的操作,因为erase()
的复杂度为 O ( n ) O(n) O(n), 所以在最坏的情况下复杂度可以到达 O ( n 2 ) O(n^2) O(n2)
而对于数组优化来说, 虽免去了哈希表,没了计数的功效。但是我们只是去做了遍历的操作,中间循环的过程使用的是
kinds
作的标记操作,不涉及erase()
,因此最坏的复杂度因为 O ( n ) O(n) O(n)
- 空间复杂度:
对于空间复杂度来说,两种优化的方法并没有涉及额外空间的申请, 所以空间复杂度即为: O ( 1 ) O(1) O(1)
Code
接下去是代码部分
- 首先的话是有关【滑动窗口】优化的代码
class Solution {
public:int totalFruit(vector<int>& fruits) {int n = fruits.size();unordered_map<int, int>hash;int max_len = INT_MIN;for(int left = 0, right = 0; right < n; ++right){// 1.进窗口hash[fruits[right]]++;// 当水果的种类多于2种的时候,开始出窗口if(hash.size() > 2){// 2.出窗口【此时left不能后移,因此要删除该水果】hash[fruits[left]]--;// 如果当前水果种类的数量到0的话,将其从哈希表中删除if(hash[fruits[left]] == 0)hash.erase(fruits[left]);left++;}// 更新结果max_len = max(max_len, right - left + 1);}return max_len == INT_MIN ? 0 : max_len;}
};
- 然后的话是有关【数组优化】的代码
class Solution {
public:int totalFruit(vector<int>& fruits) {int n = fruits.size();//unordered_map<int, int> hash;int hash[100001] = { 0 };int max_len = INT_MIN;for(int left = 0, right = 0, kinds = 0; right < n; ++right){// 一进来判断发现水果的种类为0的话,则水果种类增加一种if(hash[fruits[right]] == 0) kinds++;// 1.进窗口hash[fruits[right]]++;// 判断当前哈希表中水果的种类是否超过两种if(kinds > 2){// 2.出窗口hash[fruits[left]]--;// 如果在出窗口之后当前水果的数量为0的话,则从哈希表中删除该水果if(hash[fruits[left]] == 0)// hash.erase(fruits[left]);kinds--;left++;}// 3.更新最大长度max_len = max(max_len, right - left + 1);}return max_len == INT_MIN ? 0 : max_len;}
};
相关文章:

【滑动窗口】篮里到底能装 “几个水果” 呢?
Problem: 904. 水果成篮 文章目录 题目分析算法原理分析暴力枚举 哈希表滑动窗口优化数组再度优化 复杂度Code 题目分析 首先我们来分析一下本题的思路 首先我们通过题目的描述来理解一下其要表达的含义,题目给到我们一个fruit数组,里面存放的是每棵树上…...

newstarctf2022week2
Word-For-You(2 Gen) 和week1 的界面一样不过当时我写题的时候出了个小插曲 连接 MySQL 失败: Access denied for user rootlocalhost 这句话印在了背景,后来再进就没了,我猜测是报错注入 想办法传参 可以看到一个name2,试着传参 发现有回显三个字段…...
集群调度-01
目录 1、调度约束 2、Pod 是 Kubernetes 的基础单元,Pod 启动典型创建过程如下 2.1 工作机制 **** 2.2 调度过程 *** 2.3 Predicate 有一系列的常见的算法可以使用: ** 2.4 指定调度节点 1、调度约束 Kubernetes 是通过 List-Watch **…...
【软件工程】金管局计算机岗位——软件测试的分类(⭐⭐⭐⭐)
软件工程 软件测试的分类从是否关心软件内部结构和具体实现的角度划(⭐⭐⭐⭐)从是否执行代码角度划分(⭐⭐)从软件开发的过程按阶段划分(⭐⭐⭐⭐) 软件测试的分类 考点导读: 软件测试是软件工…...

Halcon WPF 开发学习笔记(1):Hello World小程序
文章目录 文章专栏视频链接Hello World训练图片训练目的 开始训练图像预处理导入图像三通道处理调用算子通道选取 滤波什么是好的滤波 增加对比度 区域选取阈值处理算子参数选择运行结果(红色为选择区域) 区域分割运行结果 特征筛选参数代码第二次,面积筛选 画选中十…...

pix2tex - LaTeX OCR 安装使用记录
系列文章目录 文章目录 系列文章目录前言一、安装二、使用三、少侠请留步,点赞、收藏、关注 前言 项目地址:这儿 一、安装 版本要求 Python: 3.7 PyTorch: >1.7.1 安装:pip install "pix2tex[gui]" 注意:Pyside6…...

前端框架Vue学习 ——(四)Axios
文章目录 Axios 介绍Axios 入门Vue项目中使用 Axios Axios 介绍 介绍: Axios 对原生的 Ajax 进行了封装,简化书写,快速开发。(异步请求) 官网: https://www.axios-http.cn/ 官网介绍:Axios 是一个基于 promise 网络请…...
将json数据导入到ES集群——解决方案对比填坑日记
需求 将写好的json数据。导入到es集群 数据说明 文件JSON数据,一行一个JSON。 {"id":"d2716ae8fba4e026c4bd9445c3f49e2c","lang":"zh","title":"吉美旅馆","content":"吉美..."}…...
C语言----------#pragma预处理分析
一、#pragma预处理分析 1、#pragma是编译器指示字,用于指示编译器完成一些特定的动作; 2、#pragma所定义的很多指示字是编译器和操作系统特有的; 3、#pragma在不同的编译器间是不可移植的: 预处理器将忽略它不认识的#pragma指…...
数据库中的时间django转换成None
原因 数据库中使用的是datetime[64] 的格式。精确的毫秒了。django默认的使用的是datetime.datetime.fromisoformat转换的。转换不了 使用原生查找 for raw in StockNominate.objects.raw("select id,code,strftime(%Y-%m-%d,date) as date from table_name; "):pr…...
八种流行的网络协议
1、HTTP(超文本传输协议),HTTP 是一种用于获取 HTML 文档等资源的协议。它是 Web 上任何数据交换的基础,是一种客户端 - 服务器协议。 2、HTTP/3,HTTP/3 是 HTTP 的下一个重大修订版。它运行在 QUIC 上,QU…...

Qwt QwtKnob绘制旋钮
1.简介 QwtKnob是Qwt库中的一个类,用于绘制一个旋钮样式的仪表盘。它继承QwtAbstractSlider类,提供了一些额外的功能和样式,用于旋转和选择值。 以下是类继承关系: 2.常用方法 旋钮(Knob)相关的属性和方法…...

docker部署elk
目录 前言 一、创建程序工作路径 二、创建私有网络 三、部署elasticsearch 1.先搜速后下载 2.创建一个基础的容器(此步骤是为了拷贝容器里的文件) 3.拷贝文件到宿主机 3.1进入容器 3.2拷贝并授权 3.3删除基础容器 4.创建容器 5.访问9200测试 …...
护网蓝队初级面试题摘录(下)
小王学习录 1.设备误报如何处理?2.讲一下TOP10都有哪些3.SQL注入的原理和漏洞产生的原因?4.SQL注入的类型盲注类型: 5.简单讲一下防范SQL注入的方法和原理6.SQL注入有哪些绕过姿势?7.SQL注入攻击有哪些危害?6.XSS&…...

通过51单片机控制SG90舵机按角度正反转转动
一、前言 本文介绍如何通过51单片机控制SG90舵机实现角度的正反转转动。SG90舵机是一种常用的微型舵机,具有体积小、重量轻、结构简单等特点,被广泛应用于机器人、遥控模型和各种自动控制系统中。 使用51单片机(STC89C52)作为控…...

uniapp写一个计算器用于记账(微信小程序,APP)
提要:自己用uniapp写了一个记账小程序(目前是小程序),写到计算器部分,在网上找了别人写的计算器,大多数逻辑都是最简单的,都不能满足一个记账计算器的基本逻辑。与其在网上找来找去,…...

前端的几种网络请求方式
网络请求 node编写接口 这里用到的几个包的作用 express:基于 Node.js 平台,快速、开放、极简的 Web 开发框架,官网:https://www.expressjs.com.cn/cors:用来解决跨域问题body-parser:可以通过 req.body…...

Kubernetes技术与架构-存储 4
如上所示,Kubernetes集群支持动态申请存储资源,即集群管理员可以按照实际的需求动态地申请存储资源,集群管理员需要事先定义一个或者多个StorageClass存储类型的资源,Pod中的容器实例直接引用事先定义的StorageClass存储类型的资源…...

jbase编译与部署的优化
上一篇的演示只是涉及自动编译业务脚本。演示时候工程编译是超级慢的。因为把静态资源放在了Web工程下,每次编译都要拷贝,运行起码是1分钟,不能忍受,为此思考工程结构改解决这个问题,顺带方便开发的发布。运行WebLoade…...

Filter 和 Listener
Filter 表示过滤器。是JavaWeb三大组件(Servlet、Filter、Listener)之一。 过滤器可以把对资源的请求 拦截 下来。浏览器可以访问服务器上所有的资源,而在访问到这些资源之前可以使用过滤器拦截下来,也就是说在访问资源之前会先经…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...