力扣刷题-数组-滑动窗口法相关题目总结

news2024/11/17 21:47:32

209. 长度最小的子数组(最小滑窗)

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
注意题目是返回长度即可。

暴力法

class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        ## 难的问题 可以先考虑暴力解法(若没有要求时间复杂度)
        min_len = float('inf') # 先定义一个无穷大 min_len 后续再迭代更新
        for i in range(len(nums)):
            sum = 0
            for j in range(i, len(nums)):
                sum += nums[j]
                if sum >= target:
                    min_len = min(min_len, j-i+1)
                    break # 使用break 防止再遍历下去
        
        return min_len if min_len != float('inf') else 0

时间复杂度:O(n^2)
空间复杂度:O(1)
image.png

滑动窗口法

接下来就开始介绍数组操作中另一个重要的方法:滑动窗口。
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
那么滑动窗口如何用一个for循环来完成这个操作呢。
首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?
此时难免再次陷入 暴力解法的怪圈。
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。
那么问题来了, 滑动窗口的起始位置如何移动呢?
这里还是以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:

其实从动画中可以发现滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。
在本题中实现滑动窗口,主要确定如下三点:

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
解题的关键在于 窗口的起始位置如何移动,如图所示:
image.png
可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        min_len = float("inf") # 同样也是先初始化一个min_len
        i, sum = 0, 0
        for j in range(len(nums)):
            sum += nums[j]
            while sum >= target:
                min_len = min(min_len, j-i+1)
                sum -= nums[i]
                i += 1 # 这两行是滑动窗口的巧妙之处
        return min_len if min_len != float("inf") else 0

区分时间复杂度:
暴力解题法:O(n^2) 因为第一个for循环在第一个元素的时候,第二个for循环会遍历所有元素;第一个for循环在第二个元素的时候也会遍历在第二个for循环的时候遍历所有元素…)——> n*n=n^2
滑动窗口法:不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
子串问题优先滑动窗口。

滑动窗口法模板

一般什么时候会考虑使用 滑动窗口法 ?——>答:当求子串问题(最小长度/最大长度,对应最小滑窗/最大滑窗)时候常常考虑使用滑动窗口法。 若需要种类计数,记得定义一个种类计数器以及种类计数
参考:
作者:HelloPGJC
链接:https://leetcode.cn/problems/fruit-into-baskets/solutions/1437444/shen-du-jie-xi-zhe-dao-ti-he-by-linzeyin-6crr/
来源:力扣(LeetCode)

最小滑窗

最小滑窗模板:给定数组 nums,定义滑窗的左右边界 i, j,求满足某个条件的滑窗的最小长度。

while j < len(nums):
    判断[i, j]是否满足条件(如果是简单的 一般不需要判断条件 如上面的题)
    while 满足条件:
        不断更新结果(注意在while内更新!所谓结果就是求的长度)
        i += 1 (最大程度的压缩i,使得滑窗尽可能的小)
    j += 1
最大滑窗

最大滑窗模板:给定数组 nums,定义滑窗的左右边界 i, j,求满足某个条件的滑窗的最大长度。

while j < len(nums):
    判断[i, j]是否满足条件
    while 不满足条件:
        i += 1 (最保守的压缩i,一旦满足条件了就退出压缩i的过程,使得滑窗尽可能的大)
    不断更新结果(注意在while外更新!)
    j += 1

如上面的题:

class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        min_len = float("inf") # 同样也是先初始化一个min_len
        i, sum = 0, 0
        for j in range(len(nums)):
            sum += nums[j]
            while sum >= target:
                min_len = min(min_len, j-i+1)
                sum -= nums[i]
                i += 1 # 这两行是滑动窗口的巧妙之处
        return min_len if min_len != float("inf") else 0

76. 最小覆盖子串(最小滑窗)

题目难度:困难
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:
输入:s = “ADOBECODEBANC”, t = “ABC”
输出:“BANC”
解释:最小覆盖子串 “BANC” 包含来自字符串 t 的 ‘A’、‘B’ 和 ‘C’。
示例 2:
输入:s = “a”, t = “a”
输出:“a”
解释:整个字符串 s 是最小覆盖子串。
示例 3:
输入: s = “a”, t = “aa”
输出: “”
解释: t 中两个字符 ‘a’ 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
求最小子串问题——>滑动窗口法
解析题意:
最小字串——>滑动窗口法
找出的s的子串需要涵盖t中的字符——>会使用字典计数器

class Solution(object):
    def minWindow(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        i, j = 0, 0
        needMap = defaultdict(int) # 定义一个字典计数器(对字符, 初始化都是0)
        needCnt = len(t) # 最开始 在s中我需要涵盖的字符数就是t中总的字符数
        res = "" # 结果 初始化为 ""
        for c in t:
            needMap[c] += 1 # 为字符计数器赋值
        while j < len(s):
            if s[j] in needMap:
                if needMap[s[j]] > 0:
                     needCnt -= 1 # 有这个字符 那么我需要的字符数就减1
                needMap[s[j]] -= 1 # 因为t中可能有重复字符 所以这个需要减1
            while needCnt == 0:
                if not res or j-i+1 < len(res): # res为空(前面没有结果) 注意是小于len(res) 因为是最小子串
                    res = s[i:j+1] # 获取
                # 上面只是更新了结果 needMap和needCnt还没有更新
                if s[i] in needMap:
                    if needMap[s[i]] == 0:
                        needCnt += 1 # 因为我下一步要移动i 所以如果现在没有这个字符 我需要的字符数加1
                    needMap[s[i]] += 1 # 因为我下一步要移动i 所以如果现在没有这个字符 就要加1 告诉后面我需要
                i += 1 # 更新 i
            j += 1 # 更新j
        return res

904. 水果成篮(最大字串)

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树的种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。
解题:
像这种就需要先翻译一下题目意思,就是让你从一个整数数组中,找出其中最长的子数组,然后一个限制条件是,这个最长子数组中的整数最多只能有两种。
本题返回的是子数组长度,不是子数组,注意分清楚
示例 1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。
示例 2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。
示例 3:
输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。

class Solution(object):
    def totalFruit(self, fruits):
        """
        :type fruits: List[int]
        :rtype: int
        """
        # 初始化
        i, j = 0, 0
        res = 0 # 要求最大 我先初始化为0
        classMap = defaultdict(int) # 因为涉及到种类数 所以需要一个种类计数器
        classCnt = 0 # 因为是求最大 我刚开始计数当然为0

        while j < len(fruits):
            if classMap[fruits[j]] == 0:
                classCnt += 1 # 第一个数 种类肯定是加1
            classMap[fruits[j]] += 1
            while classCnt > 2: # 不符合条件 种类数超过2
                # if classMap[fruits[i]] > 0: # 不能用0判断 因为不知道种类数能不能减1
                if classMap[fruits[i]] == 1:
                    classCnt -= 1 # 因为下一步我就要移动i了, 当前为1 移动一下肯定为0
                classMap[fruits[i]] -= 1 # 该种类数计数也减1
                i += 1
            res = max(res, j-i+1) # 在while外面更新结果
            j += 1
        return res

理解最小滑窗和最大滑窗的更新的不同之处:
image.png
因为是符合条件才更新结果,最小滑动窗口中,while的条件是 满足条件 因此在while内更新,而最大滑动窗口中,while的条件是 不满足条件 因此得等到条件满足,也就是退出循环后再更新

1004. 最大连续1的个数 III

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。
示例 1:
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
注意,要理清题意,这里未涉及种类计数,其中 条件 就是 0的数量 因为翻转0是题目已经设定好的。

class Solution(object):
    def longestOnes(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        # 求数组中的子数组 最大个数 ——> 最大滑窗
        # 限制条件是 最大翻转k个0 需要0的个数
        # 想复杂了 还是要理清题意
        i, j = 0, 0
        res = 0
        zeroCnt = 0
        while j < len(nums):
            if nums[j] == 0:
                zeroCnt += 1
            while zeroCnt > k: # 注意是k
                if nums[i] == 0:
                    zeroCnt -= 1 # 因为我下一步要移动i
                i += 1
            res = max(res, j-i+1)
            j += 1
        return res 

3. 无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: s = “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。

class Solution(object):
    def lengthOfLongestSubstring(self, s):
        """
        :type s: str
        :rtype: int
        """
        # 子串 最大长度 ——> 最大滑窗
        # 不含重复字符 说明遇到字符计数
        # 巧妙之处:将子串中的字符数 与 字典长度联系起来
        # 模板不要用的太死板

        i, j = 0, 0
        res = 0
        char_dict = defaultdict(int)
        while j < len(s):
            char_dict[s[j]] += 1
            while len(char_dict)<j-i+1: # 不满足条件 这个比较难想
                char_dict[s[i]] -= 1 # 下一步就是移动i
                if char_dict[s[i]] == 0:
                    del char_dict[s[i]]
                i += 1
            if len(char_dict) == j - i + 1:  # 巧妙之处
                res = max(res, j-i+1)
            j+=1
        return res

参考:https://programmercarl.com/

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

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

相关文章

uniapp确认提示框;uniapp判断输入框值是否符合正常手机号,身份证号

确认提示框 UniApp 中&#xff0c;你可以使用 uni.showModal 方法来创建确认提示框。以下是一个示例&#xff1a; <template><view class"container"><button click"showAuthModal">显示确认提示框</button></view> </…

react实现动态递增展示数字特效

在可视化展示界面时有一种场景&#xff0c;就是页面在初始化的时候&#xff0c;有些数字展示想要从某个值开始动态递增到实际值&#xff0c;形成一种动画效果。例如&#xff1a; 写一个数字递增的组件&#xff0c;代码如下&#xff1a; import {useEffect, useRef, useState} f…

软考网络工程师IPSEC VPN配置考点总结

IPSEC VPN&#xff08;华为&#xff09;工作流程 配置安全ACL&#xff1a;配置哪些流量需要被保护配置安全提议&#xff1a;配置IPsec的参数配置IKE&#xff1a;预共享密钥&#xff0c;配置身份验证方法、加密算法等安全参数配置安全策略&#xff1a;1和2做关联在接口应用安全…

文件系统之程序是怎么打开文件进行操作的

本篇文章自顶向下&#xff0c;从文件系统的上层出发讲到磁盘&#xff0c;帮助理解程序是如何打开文件并进行后序的读写操作的&#xff0c;读到后面&#xff0c;前面的一些疑惑就得到解决 介绍相关概念 注意&#xff0c;目录也是文件 文件描述符 每个进程都有一个指针*files…

Postman接口测试完整篇,全网唯一

前言 今天给大家分享的内容是接口测试必备的postman测试工具的使用&#xff1a;postman发送get与post请求&#xff0c;变量的设置与引用&#xff0c;文件的导入与导出&#xff0c;断言机制&#xff0c;参数化&#xff08;数据驱动&#xff09;&#xff0c;批量执行测试集&…

算法通关村 | 透彻理解动态规划

1. 斐波那契数列 1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13&#xff0c;..... f(n) f(n-1) f(n-2) 代码实现 public static int count_2 0;public int fibonacci(int n){if (n < 2){count_2;return n;}int f1 1;int f2 2;i…

探索单链表数据结构:理解与实现

文章目录 &#x1f34b;引言&#x1f34b;什么是单链表&#xff1f;&#x1f34b;单链表的基本操作&#x1f34b;单链表的实现&#x1f34b;练习题&#x1f34b;总结 &#x1f34b;引言 在计算机科学和数据结构中&#xff0c;链表是一种基本且重要的数据结构&#xff0c;用于存…

基于springboot财务管理系统springboot006

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

【zabbix监控三】zabbix之部署代理服务器

一、部署代理服务器 分布式监控的作用 分担server的几种式压力解决多机房之间的网络延时问题 1、搭建proxy主机 192.168.88.50 关闭防火墙和安全机制&#xff0c;修改主机名 设置 zabbix 的下载源&#xff0c;按照 zabbix-proxy rpm -ivh \ https://mirrors.aliyun.com/zab…

01-Maven入门

1 Maven简介 1.1 Maven是什么 Maven 是一个用于构建和管理 Java 项目的工具。它提供了一种标准化的项目结构和构建流程&#xff0c;可以自动化地处理项目的依赖管理、编译、测试、打包和部署等任务。 Maven 使用一个基于 XML 的配置文件&#xff08;pom.xml&#xff09;来描…

【C++面向对象侯捷】12.虚函数与多态 | 13.委托相关设计【设计模式 经典做法,类与类之间关联起来,太妙了,不断的想,不断的写代码】

文章目录 12.虚函数与多态举例&#xff1a;委托 继承【观察者模式】13.委托相关设计Composite 组合模式Prototype 原型模式 12.虚函数与多态 纯虚函数 一定要 子类重新定义的 继承和复合 关系下的构造和析构 举例&#xff1a;委托 继承【观察者模式】 13.委托相关设计 问题…

建材行业微信小程序开发实战经验分享

随着互联网的迅猛发展&#xff0c;电子商务成为了人们日常购物的主要方式之一。而微信小程序的兴起更是为商家提供了一个全新的线上销售渠道。在建筑材料选购领域&#xff0c;开发一个微信小程序商城平台能够有效地提升用户的购物体验和商家的销售效益。 为了实现建筑材料选购平…

Android studio中如何下载sdk

打开 file -> settings 这个页面, 在要下载的 SDK 前面勾上, 然后点 apply 在 platforms 中就可以看到下载好的 SDK: Android SDK目录结构详细介绍可以参考这篇文章: 51CTO博客- Android SDK目录结构

OpenAI ChatGPT API 文档之 Embedding

译者注&#xff1a; Embedding 直接翻译为嵌入似乎不太恰当&#xff0c;于是问了一下 ChatGPT&#xff0c;它的回复如下&#xff1a; 在自然语言处理和机器学习领域&#xff0c;"embeddings" 是指将单词、短语或文本转换成连续向量空间的过程。这个向量空间通常被称…

Python程序设计实例 |爬取网络中的小说

网络文学是新世纪我国流行文化中的重要领域&#xff0c;年轻人对网络小说更是有着广泛的喜爱。本文以抓取网络小说正文为例编写一个简单、实用的爬虫脚本。 01、分析网页 很多人在阅读网络小说时都喜欢本地阅读&#xff0c;换句话说就是把小说下载到手机或者其他移动设备上阅读…

TQ210-Bootloader-Uboot(LTS)

Bootloader的作用 Bootloader是位于计算机系统启动过程中的程序&#xff0c;它的主要作用是将操作系统从磁盘等外部存储介质加载到计算机内存中&#xff0c;并启动操作系统执行。Bootloader通常包括硬件初始化、自检、异常处理和启动操作系统等功能。它是计算机系统中非常重要…

Mac电脑系统怎么样才能干干净净地卸载应用程序?

Mac系统怎么样才能干干净净地卸载应用程序&#xff0c;不留下隐私数据和用户信息呢&#xff1f;如果有方法的话&#xff0c;那么该方法对于Mac电脑小白是否友好呢&#xff1f; CleanMyMac就是一款用于清理Mac系统下应用程序的一款清理工具&#xff0c;其内置了应用程序的安全卸…

第二证券:智能网联汽车产业迎催化 容量电价政策出台可期

昨日&#xff0c;A股延续调整态势&#xff0c;沪指失守3100点&#xff0c;深成指跌破10000点大关&#xff0c;创业板跌约1%再创阶段新低&#xff1b;两市成交额保持在地量水平&#xff0c;再创年内新低。到收盘&#xff0c;沪指跌0.77%报3084.7点&#xff0c;深成指跌0.9%报998…

速卖通新品如何推广,速卖通的推广渠道有哪些?——站斧浏览器

速卖通的推广渠道非常多样化&#xff0c;卖家可以根据自己的需求和预算选择合适的渠道来推广产品&#xff0c;提高曝光度和销售量&#xff0c;能够有效地提高产品的知名度和信任度。 速卖通新品如何推广&#xff1f; 速卖通上有数以百万计的卖家&#xff0c;每天都有大量的新…

批量寄件教程

快递行业的发展&#xff0c;和企业之间其实是正向的影响。为什么这么说呢&#xff1f;企业因公寄件&#xff0c;能为快递公司贡献一定寄件量&#xff0c;而快递行业的发展&#xff0c;不管是运输速度的提升&#xff0c;服务质量的提高&#xff0c;都能为企业的发展提供帮助&…