【滑动窗口】篮里到底能装 “几个水果” 呢?

news2024/10/6 13:37:14

在这里插入图片描述

Problem: 904. 水果成篮

文章目录

  • 题目分析
  • 算法原理分析
    • 暴力枚举 + 哈希表
    • 滑动窗口优化
    • 数组再度优化
  • 复杂度
  • Code

题目分析

首先我们来分析一下本题的思路

  • 首先我们通过题目的描述来理解一下其要表达的含义,题目给到我们一个fruit数组,里面存放的是每棵树上水果的数量。当我们拿着两个篮子去采摘水果的时候,可以选择任意一颗树开始采摘
  • 虽然篮子里面只能装一种水果,即两个篮子只能有两种水果,但是每种水果所装的个数是不受限制的,所以只要水果的种类不超过两种即可

1.jpg

  • 我们再来仔细看一下示例,比如这个示例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个

2.jpg

💬 那么本题我们就转换为了:找出一个最长的子数组长度,子数组中不超过两种类型的水果

算法原理分析

接下去我们就来分析一下本题的算法原理

暴力枚举 + 哈希表

  • 首先最先相当的一定是暴力枚举,从第一个数开始往后进行枚举,以此类推,找到符合条件且长度最长的那一个。将做遍历到的加入到哈希表中,在遍历的过程中逐步进行判断即可
  • 对于暴力解法的代码这里就不展示了,读者可以自己尝试着去写写看

3.jpg

滑动窗口优化

  • 我们主要还是来讲一讲有关如何使用【滑动窗口】去做优化的事情,那对于滑动窗口而言我们知道是基于『双指针』的,所以在这里我们会需要有一个left指针和一个right指针,当这个right指针在向后移动的时候,当其无法在继续后移的时候表示[left, right]这段区间内的水果数量已经到达②了,此时我们要考虑去做【出窗口】的操作

4.jpg

  • 那这个时候我们让left指针向右移动一格,那此时读者可以思考一下当前的kinds会出现什么样的变化?

5.jpg

  • 在下面我列举出了两种,一个是 kinds不变的情况,因为其在后移的过程中,所取消掉的那一个水果的种类可能在[left, right]这个区间中还存在着这个种类的水果
  • 第二个则是 kinds变小的情况,很好理解,若是在[left, right]区间中不包含了去除掉的这种水果的话,说明种类就会减少

6.jpg

  • 与之相对应的我们要给出right指针相应的变化情况,若是kinds不做变化的话,right也无需去变化,因为再去右移的话可能会增加水果的种类;若是kinds变小的话,那么right就可以右移了,此时水果的种类就可以增加上去

那接下去呢,就让我们来看看,算法的执行过程是怎样的

  • 在这里,我们的哈希表中所存的数据种类有两个,一个是 当前的水果种类,另一个呢则是 这个水果所对应的数量
  • 所以当我们使用右指针right在进行遍历的时候,其所对应的个数就++,那当这个数量大于2时就开始出窗口,所对应的则是left左指针所指向的水果个数,但是呢这不是随便减的,当其减到【0】的时候,就要考虑从这个哈希表中删除掉这个相对应的水果了。
  • 最后当出完窗口后我们要做的就是去更新这个最长的结果

7.jpg

好,以上就是有关【滑动窗口】相关的算法原理分析,详情代码见【Code】

💬 那有同学问:为何不直接讲滑动窗口相关的算法呢,而是每次都要讲一下暴力的解法,这不是多此一举吗?

  • 同学,你应该要明白,我们在笔试面试时做算法题的时候,不是一看到一道题的时候就会想到它到底需要使用什么算法与数据结构,一般在拿到一道题的时候我们一般都会先去考虑暴力的解法。在思考一段时间之后才会去思考其是都可以一些算法来进行解决,我们平常在刷题的过程中,重要的不是你马上就能想到这个算法,我们更加关注的是这个解题的过程,大家在练习算法题的时候一定要格外注意

数组再度优化

有读者一定会疑惑,已经使用【滑动窗口】去做了优化为什么还要再去优化呢?我们可以来看看滑动窗口的代码提交之后的结果

  • 可以看到效率并不是很高,原因其实就在于我们在频繁地去出入窗口

8.jpg

💬 那怎么去做一个优化呢?

  • 我们再来观察一下题目给到我们的提示,这个fruit数组最大的个数为 $ 10^5 $,那我们其实可以不用使用哈希表去存储,而是直接利用【数组】去进行存放,因为对于数组的每个元素来说其实就是一种映射,和哈希表其实是差不多的原理

9.jpg

  • 我们可以选择直接将数组的大小定为这个大小,并且呢我们需要在循环中放一个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】部分,我们来看到提交之后的执行结果可以观察到性能确实得到了大幅度的提升

10.jpg

复杂度

接下去我们来分析一下时间复杂度

  • 时间复杂度:

首先是对于时间复杂度而言,【滑动窗口】部分的代码, 我们使用leftright双指针去遍历查找整个数组, 并且在查找的过程中遇到水果数量 > 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;
    }
};

在这里插入图片描述

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

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

相关文章

Linux Vim批量注释和自定义注释

使用 Vim 编辑 Shell 脚本&#xff0c;在进行调试时&#xff0c;需要进行多行的注释&#xff0c;每次都要先切换到输入模式&#xff0c;在行首输入注释符"#"再退回命令模式&#xff0c;非常麻烦。连续行的注释其实可以用替换命令来完成。 换句话说&#xff0c;在指定…

推特被封号怎么办?如何防封?

今年社交媒体巨头Twitter正式更名与标示为“X”&#xff0c;这一举措引发了广泛关注和讨论。马斯克称&#xff0c;此举是为了将推特重塑为一个广泛的通信和金融交易平台&#xff0c;打造一个像“微信”一样的万能应用程序&#xff0c;也就是“X”&#xff0c;并承诺推特将迅速进…

外汇天眼:全员免费,赢奖金!

外汇市场一直以来都是金融投资者的热门领域之一&#xff0c;但对于新手来说&#xff0c;了解和掌握外汇交易可能需要时间和经验。为了帮助新手入门&#xff0c;提高交易技能&#xff0c;外汇模拟交易应运而生。为的是能够零风险无压力地帮助外汇投资者更好地掌握外汇交易的技巧…

SonarQube的使用心得

一、使用背景&#xff1a; SonarQube 是一个用于代码质量管理的开源平台&#xff0c;用于管理源代码的质量。 通过插件形式&#xff0c;可以支持包括 java, C#, C/C, PL/SQL, Cobol, JavaScrip, Groovy 等等二十几种编程语言的代码质量管理与检测。 Sonar可以从以下七个维度…

给定n个点或一个凸边形,求其最小外接矩形,可视化

这里写目录标题 原理代码 原理 求n个点的最小外接矩形问题可以等价为先求这n个点的凸包&#xff0c;再求这个凸包的最小外接矩形。 其中求凸包可以使用Graham-Scan算法 需要注意的是&#xff0c; 因为Graham-Scan算法要求我们从先找到凸包上的一个点&#xff0c;所以我们可以先…

CRM 报告:跟踪销售业绩的强大工具

对于希望保持良好客户关系的企业来说&#xff0c;CRM&#xff08;客户关系管理&#xff09;报告是不可或缺的。它使企业能够跟踪客户互动&#xff0c;并利用这些数据改善客户服务。 需要注意的是&#xff0c;CRM 报告不是一次性的&#xff0c;而是一个持续的过程。这是因为客户…

Webpack介绍大全

Webpack 一 、什么是webpack WebPack是一个现代JS应用程序的静态模块打包器&#xff08;module bundler&#xff09; 模块&#xff08;模块化开发&#xff0c;可以提高开发效率&#xff0c;避免重复造轮子&#xff09; 打包&#xff08;将各个模块&#xff0c;按照一定的规则…

“第六十二天”

新东西 %[^\n] 这个题测试的时候不知道哪里为什么一直错一个。 int main() {char a[81] { 0 };fgets(a, 80, stdin);int i 0;int n strlen(a);for(i0;i<n;i){if ((a[i] > a && a[i] < y) || (a[i] > A && a[i] < Y))a[i] 1;else if (a[i…

中介模式(Mediator)

简介 当各个模块的调用变得错综复杂时&#xff0c;可以使用中介模式&#xff0c;用一个中介对象完成对象交互&#xff0c;各个对象不需要显示的相互引用。 创建一个中介对象完成所有的调用&#xff1a;Mediator->A ,Mediator->B,Mediator->C,Mediator->D&#xf…

设计模式—结构型模式之桥接模式

设计模式—结构型模式之桥接模式 将抽象与实现解耦&#xff0c;使两者都可以独立变化。 在现实生活中&#xff0c;某些类具有两个或多个维度的变化&#xff0c;如图形既可按形状分&#xff0c;又可按颜色分。如何设计类似于 Photoshop 这样的软件&#xff0c;能画不同形状和不…

gcov c++代码覆盖率测试工具(原理篇)

一、gcov简单介绍 Gcov是一个测试C/C代码覆盖率的工具&#xff0c;伴随GCC发布&#xff0c;配合GCC共同实现对C/C文件的语句覆盖、功能函数覆盖和分支覆盖测试。 二、gcov统计生成覆盖率流程 图1 gcov覆盖率生成过程 Gcc在编译阶段指定 –ftest-coverage 等覆盖率测试选项后…

8-2、T型加减速计算简化【51单片机控制步进电机-TB6600系列】

摘要&#xff1a;本节介绍简化T型加减速计算过程&#xff0c;使其适用于单片机数据处理。简化内容包括浮点数转整型数计算、加减速对称处理、预处理计算 一、浮点数转整型数计算 1.1简化∆t_1计算 根据上一节内容已知 K0.676 step1.8/X&#xff08;x为细分值&#xff0c;1.8对…

Oracle(11)Managing Tables

Managing Tables 管理表 目标&#xff1a; 识别存储数据的各种方法概述甲骨文数据类型区分扩展ROWID与限制ROWID勾勒出一行的结构创建常规表和临时表管理表中的存储结构重新组织、截断和删除表删除表中的列 一、基础知识 1、Oracle Built-in Data Types Oracle内置数据类型 2…

泛微OA_lang2sql 任意文件上传漏洞复现

简介 泛微OA E-mobile系统 lang2sql接口存在任意文件上传漏洞&#xff0c;由于后端源码中没有对文件没有校验&#xff0c;导致任意文件上传。攻击者可利用该参数构造恶意数据包进行上传漏洞攻击。 漏洞复现 FOFA语法&#xff1a; title"移动管理平台-企业管理" 页…

jbase编译与部署的优化

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

代码随想录第四十四天 | 动态规划 完全背包:纯完全背包理论基础(卡码网第52题);应用(注意遍历顺序):组合(518),排列(377)

1、动态规划&#xff1a;完全背包理论基础 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包里物品价值总和最大…

详解基于Android的Appium+Python自动化脚本编写

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

排序算法之-选择

算法原理 在未排序的数列中找出最大&#xff08;或最小&#xff09;的元素&#xff0c;然后将其存入到已排序的数列起始位置&#xff0c;紧接着在剩余的未排序数列中继续查找最大&#xff08;或最小&#xff09;的元素&#xff0c;并将其放入到已排序的数列末尾&#xff0c;依…

数据结构:Map和Set(1)

搜索树 概念 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值 它的左右子树也分别为二叉搜索树 这棵树的中序遍历结果是有序的 接下来我们来模拟一棵二叉搜索树&#xff0c…

串口通信(3)-接收一组固定长度的数据

本文为博主 日月同辉&#xff0c;与我共生&#xff0c;csdn原创首发。希望看完后能对你有所帮助&#xff0c;不足之处请指正&#xff01;一起交流学习&#xff0c;共同进步&#xff01; > 发布人&#xff1a;日月同辉,与我共生_单片机-CSDN博客 > 欢迎你为独创博主日月同…