【十】【算法分析与设计】滑动窗口(1)

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target

找出该数组中满足其总和大于等于 target 的长度最小的 连续

子数组

[nums(l), nums(l+1), ..., nums(r-1), nums(r)] ,并返回其长度如果不存在符合条件的子数组,返回 0

示例 1:

 
 

输入:target = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4] 输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1] 输出:0

提示:

  • 1 <= target <= 10(9)

  • 1 <= nums.length <= 10(5)

  • 1 <= nums[i] <= 10(5)

进阶:

  • 如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

暴力枚举:

 
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        int ret = INT_MAX;
        for (int start = 0; start < n; start++) {
            int sum = 0;
            for (int end = start; end < n; end++) {
                sum += nums[end];
                if (sum >= target) {
                    ret = min(ret, end - start + 1);
                    break;
                }
            }
        }
        return ret == INT_MAX ? 0 : ret;
    }
};

滑动窗口(同向指针)优化暴力枚举:

对于[left,right]区间中,如果sum>=target,此时right-left+1的长度一定是和left组合中最小的长度,leftright+1....后面的组合一定都大于target,但是长度一定比right-left+1长。所以对于left的所有组合已经全部考虑完毕。注意这里有一个隐含的信息,此时nums[left]+....+nums[right-1]<target。对于与left的所有组合都考虑完毕,sum-=nusm[left],left++

此时leftleft+1...right-1,这些数的组合一定<target。因为nums[left-1]+....+nums[right-1]<target。所以这些情况可以直接无视,当作我们已经考虑完毕。只需要再从right的位置再考虑。

如果sum<target,right++,sum+=nums[right],直到sum>=target。以此类推

 
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        int ret = INT_MAX;
        int sum = 0;

        for (int left = 0, right = 0; right < n; right++) {
            sum += nums[right];
            while (sum >= target) {
                ret = min(ret, right - left + 1);
                sum -= nums[left++];
            }
        }
        return ret == INT_MAX ? 0 : ret;
    }
};

class Solution {public:int minSubArrayLen(int target, vector<int>& nums) {

定义了一个名为 minSubArrayLen 的成员函数,它接受一个目标值 target 和一个整数数组 nums 作为参数,并返回一个整数,表示所求子数组的最短长度。

int n = nums.size();int ret = INT_MAX;int sum = 0;

获取 nums 的大小并赋值给 n,初始化 retINT_MAX 作为最短长度的最大可能值,初始化 sum 为 0 用于记录窗口内元素的总和。

for (int left = 0, right = 0; right < n; right++) { sum += nums[right];

使用双指针 leftright 来定义滑动窗口的边界。right 指针向右移动来扩大窗口,并将 nums[right] 加到 sum 中。

while (sum >= target) { ret = min(ret, right - left + 1); sum -= nums[left++];}

当窗口内元素总和大于等于 target 时,尝试缩小窗口来找到最短的子数组。更新 ret 为当前子数组长度和 ret 的较小值,然后将 left 指针右移并从 sum 中减去 nums[left]

}return ret == INT_MAX ? 0 : ret;}};

最后,如果 ret 仍为 INT_MAX,表示没有找到满足条件的子数组,返回 0;否则返回 ret 作为最短子数组的长度。

时间复杂度和空间复杂度分析

时间复杂度:O(n),其中 n 是数组 nums 的长度。尽管有一个嵌套的 while 循环,但每个元素最多被访问两次(一次由 right 指针,一次由 left 指针),所以总的时间复杂度是线性的。

空间复杂度:O(1),算法只使用了常数级别的额外空间。

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

给定一个字符串 s ,请你找出其中不含有重复字符的 最长

子串

的长度。

示例 1:

 
 

输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

 
 

输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

 
 

输入: s = "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

  • 0 <= s.length <= 5 * 10(4)

  • s 由英文字母、数字、符号和空格组成

暴力枚举:

 
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int ret = 0;
        int n = s.length();
        for (int i = 0; i < n; i++) {
            int hash[128] = {0};
            for (int j = i; j < n; j++) {
                hash[s[j]]++;
                if (hash[s[j]] > 1)
                    break;

                ret = max(ret, j - i + 1);
            }
        }
        return ret;
    }
};

class Solution {public:int lengthOfLongestSubstring(string s) {

定义了一个名为 lengthOfLongestSubstring 的成员函数,它接受一个字符串 s 作为参数,并返回一个整数,表示无重复字符的最长子串的长度。

int ret = 0;int n = s.length();

初始化 ret 为 0 来记录最长子串的长度,获取字符串 s 的长度并赋值给 n

for (int i = 0; i < n; i++) {

使用外层循环遍历字符串 si 是子串的起始位置。

int hash[128] = {0};

初始化一个哈希表 hash,用于记录子串中每个字符的出现次数。这里使用了长度为 128 的数组,是因为 ASCII 字符集共有 128 个字符。

for (int j = i; j < n; j++) { hash[s[j]]++;

使用内层循环从 i 开始遍历字符串 sj 是子串的结束位置。每遍历一个字符,就在哈希表中对应位置的计数加一。

if (hash[s[j]] 1)break;

如果当前字符的出现次数超过了一次,说明子串中出现了重复字符,需要终止内层循环。

ret = max(ret, j - i + 1);}}

在每次内层循环中,如果没有发现重复字符,就更新 ret 的值为当前子串长度和原 ret 值的较大者。

时间复杂度和空间复杂度分析

时间复杂度:O(n^2),其中 n 是字符串 s 的长度。因为有两层嵌套循环,每个字符都可能被检查 n 次。

空间复杂度:O(1),尽管使用了哈希表,但其大小是固定的(128),与输入大小无关,因此空间复杂度为常数。

滑动窗口(同向双指针)优化暴力枚举:

研究[left,right]区间,如果这个区间没有重复的字符,right++,如果当right加入进来后发现有重复的字符,对于leftright+1,right+2.........这些的组合都会有重复的字符,所以我们直接跳过这些组合,对于left的所有组合都考虑完毕,left++。这里有一个隐含的信息,那就是(left还没有++时)[left,right-1]这段区间是没有重复字符的区间。(left++后)研究与left的组合情况。对于[left,right-1]这些情况一定是没有重复字符的,但是长度一定比还没有left++时的长度小,所以对于leftleft+1,left+2......right-1这些数的组合可以直接跳过。直接从right的组合开始考虑。

如果[left,right]有重复字符,那么left++,直到没有重复为止。

 
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int ret = 0;
        int n = s.length();

        int hash[128] = {0};
        int left = 0, right = 0;
        while (right < n) {
            hash[s[right]]++;
            while (hash[s[right]] > 1) {
                hash[s[left]]--;
                left++;
            }
            ret = max(ret, right - left + 1);
            right++;
        }
        return ret;
    }
};

class Solution {public:int lengthOfLongestSubstring(string s) {

定义了一个名为 lengthOfLongestSubstring 的成员函数,它接受一个字符串 s 作为参数,并返回一个整数,表示无重复字符的最长子串的长度。

int ret = 0;int n = s.length();

初始化 ret 为 0 来记录最长子串的长度,获取字符串 s 的长度并赋值给 n

int hash[128] = {0};

初始化一个哈希表 hash,用于记录子串中每个字符的出现次数。这里使用了长度为 128 的数组,是因为 ASCII 字符集共有 128 个字符。

int left = 0, right = 0;

初始化双指针 leftright 来定义滑动窗口的边界。这个窗口内将不会有重复的字符。

while (right < n) { hash[s[right]]++;

right 指针向右移动来扩大窗口,并将 nums[right] 对应的哈希表计数加一。

while (hash[s[right]] 1) { hash[s[left]]--; left++;}

当在哈希表中发现 nums[right] 对应的计数大于 1 时,说明窗口内出现了重复字符。此时对于left的所有组合可以理解为全部都考虑完毕。left++后,对于leftleft+1....right-1这些数的组合直接跳过,如果此时还有重复的字符,表明对于此left的所有情况也全部考虑完毕。继续进行left++,直到没有重复字符为止。left 指针向右移动,并将离开窗口的字符 nums[left] 对应的哈希表计数减一。

ret = max(ret, right - left + 1);

在每次 right 指针移动之后,更新 ret 为当前窗口的大小和原 ret 值的较大者。

right++;}

right 指针继续向右移动,寻找下一个可能的最长无重复字符的子串。

时间复杂度和空间复杂度分析

时间复杂度:O(n),其中 n 是字符串 s 的长度。尽管有一个嵌套的 while 循环,但每个字符最多被访问两次(一次由 right 指针,一次由 left 指针),所以总的时间复杂度是线性的。

空间复杂度:O(1),尽管使用了哈希表,但其大小是固定的(128),与输入大小无关,因此空间复杂度为常数。

1004. 最大连续1的个数 III

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k0 ,则返回 数组中连续 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。

提示:

  • 1 <= nums.length <= 10(5)

  • nums[i] 不是 0 就是 1

  • 0 <= k <= nums.length

暴力枚举:

 
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int zeroCount = 0;
        int ret = 0;
        int n = nums.size();
        for (int i = 0; i < n; i++) {
            zeroCount=0;
            for (int j = i; j < n; j++) {
                if (nums[j] == 0)
                    zeroCount++;
                if (zeroCount > k)
                    break;
                ret = max(ret, j - i + 1);
            }
        }
        return ret;
    }
};

滑动窗口(同向双指针)优化暴力枚举:

对于与left的组合,维护一个区间[left,right)表示left与left+1,left+2......right-1这些数的组合全部考虑完毕。接着考虑与right的组合情况。如果nums[right]==0,zeroCount++。当新加入的right使得zeroCount>k,表示含有的0超过了限制,leftright的组合不符合题意,当然leftright+1.....n-1的组合也不符合题意。跳过后面的组合,对于left的所有组合情况考虑完毕。left++。(left++后)leftleft+1,left+2....right-1这些数的组合符合题意,但是长度一定比还没有left++的长度小,所以对于这些组合我们也跳过。直接考虑leftright的组合。如果此时zeroCount>k,后面的组合也不用考虑,说明left的所有组合都考虑完毕了。left++,以此类推,直到zeroCount<=k。此时对于区间[left,right]已经考虑完毕,right++

 
class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int zeroCount = 0;
        int ret = 0;
        int n = nums.size();

        for (int left = 0, right = 0; right < n; right++) {
            if (nums[right] == 0)
                zeroCount++;
            while (zeroCount > k)
                if (nums[left++] == 0)
                    zeroCount--;
            ret = max(ret, right - left + 1);
        }
        return ret;
    }
};

class Solution {public:int longestOnes(vector<int& nums, int k) {

定义了一个名为 longestOnes 的成员函数,它接受一个整数数组 nums 和一个整数 k 作为参数,并返回一个整数,表示翻转最多 k 个 0 后数组中最大连续 1 的个数。

int zeroCount = 0;int ret = 0;int n = nums.size();

初始化 zeroCount 为 0 来记录当前窗口内 0 的个数,初始化 ret 为 0 来记录最大连续 1 的个数,获取数组 nums 的长度并赋值给 n

for (int left = 0, right = 0; right < n; right++) {if (nums[right] == 0) zeroCount++;

使用双指针 leftright 来定义滑动窗口的边界。right 指针向右移动来扩大窗口,并在遇到 0 时增加 zeroCount

while (zeroCount k)if (nums[left++] == 0) zeroCount--;

当窗口内 0 的个数超过 k 时,对于left的所有组合都考虑完毕,left++,对于++后的left,如果zeroCount>k,说明对于left的所有组合也考虑完毕,left++,直到zreoCount<=k,缩小窗口直到 0 的个数不超过 kleft 指针向右移动,并在移出窗口的是 0 时减少 zeroCount

ret = max(ret, right - left + 1);

每次 right 指针移动后,更新 ret 为当前窗口的大小和原 ret 值的较大者。这里的窗口大小即为连续 1 的个数(包括翻转的 0)。

时间复杂度和空间复杂度分析

时间复杂度:O(n),其中 n 是数组 nums 的长度。尽管有一个嵌套的 while 循环,但每个元素最多被访问两次(一次由 right 指针,一次由 left 指针),所以总的时间复杂度是线性的。

空间复杂度:O(1),算法只使用了常数级别的额外空间。

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

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

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

相关文章

[嵌入式系统-40]:龙芯1B 开发学习套件 -10-PMON启动过程start.S详解

目录 一、龙芯向量表与启动程序的入口&#xff08;复位向量&#xff09; 1.1 复位向量&#xff1a; 1.2 代码执行流程 1.3 计算机的南桥 VS 北桥 二、PMON代码执行流程 三、Start.S详解 3.1 CPU初始化时所需要的宏定义 &#xff08;1&#xff09;与CPU相关的一些宏定义…

[游戏开发][UE5.3]GAS学习心得

GAS(GameplayAbilitySystem) UE提供的一套技能框架&#xff0c;这个框架也不是万能的&#xff0c;甚至各个部件你要进行封装开发&#xff0c;但这也比你从头写一套技能框架要容易很多。 GAS功能极其强大&#xff0c;所以它是一个庞大的系统&#xff0c;如果想运用得当&#x…

深度学习pytorch——基本运算(持续更新)

基本运算——加、减、乘、除 建议直接使用运算符&#xff0c;函数和运算符的效果相同 代码演示&#xff1a; #%% # 加减乘除 a torch.rand(3,4) b torch.rand(4) # 这里a、b可以相加&#xff0c;别忘了pytorch的broadcast机制 print(ab) print(torch.add(a,b)) print(torc…

MySQL中的索引失效情况介绍

MySQL中的索引是提高查询性能的重要工具。然而&#xff0c;在某些情况下&#xff0c;索引可能无法发挥作用&#xff0c;甚至导致查询性能下降。在本教程中&#xff0c;我们将探讨MySQL中常见的索引失效情况&#xff0c;以及它们的特点和简单的例子。 1. **索引失效的情况** …

代码算法训练营day9 | 28. 实现 strStr() 、459.重复的子字符串

day9&#xff1a; 28. 实现 strStr()KMP的主要应用&#xff1a;什么是前缀表&#xff1a;前缀表是如何记录的&#xff1a; 如何计算前缀表&#xff1a;构造next数组&#xff1a;1、初始化2、处理前后缀不相同的情况3、处理前后缀相同的情况 代码&#xff1a; 459.重复的子字符串…

Python入门(三)

序列 序列是有顺序的数据集合。序列包含的一个数据被称为元素&#xff0c;序列可以由一个或多个元素组成&#xff0c;也是可以没有任何元素的空序列。 序列的类型 元组&#xff08;定值表&#xff09;&#xff1a;一旦建立&#xff0c;各个元素不可再更变&#xff0c;所以一…

Linux文件操作

pwd命令 cd命令 ls命令 mkdir命令 同时创建父子目录 cp命令 mv命令&#xff08;相当于用cp复制之后&#xff0c;把源文件删除&#xff09; 用mv命令来冲命令 rm命令 可以看到&#xff0c;我们用当前目录的文件覆盖了目标路径上的文件&#xff0c;并且目标路径中多了一个以波浪…

5 张图带你了解分布式事务 Saga 模式中的状态机

大家好&#xff0c;我是君哥。 状态机在我们的工作中应用非常广泛&#xff0c;今天聊一聊分布式事务中间件 Seata 中 Saga 模式的状态机。 1 状态机简介 状态机是一个数学模型&#xff0c;它将工作中的运行状态和流转规则抽象出来&#xff0c;可以协调相关信号来完成预先设定…

构造-析构-拷贝构造-赋值运算符重载-const成员函数

1. 类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么时候都不写时&#xff0c;编译器会自动生成以下6个成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器…

C++之deque与vector、list对比分析

一.deque讲解 对于vector和list&#xff0c;前一个是顺序表&#xff0c;后一个是带头双向循环链表&#xff0c;前面我们已经实现过&#xff0c;这里就不再讲解了&#xff0c;直接上deque了。 deque&#xff1a;双端队列 常见接口大家可以查看下面链接&#xff1a; deque - …

STM32第九节(中级篇):RCC(第一节)——时钟树讲解

目录 前言 STM32第九节&#xff08;中级篇&#xff09;&#xff1a;RCC——时钟树讲解 时钟树主系统时钟讲解 HSE时钟 HSI时钟 锁相环时钟 系统时钟 SW位控制 HCLK时钟 PCLKI时钟 PCLK2时钟 RTC时钟 MCO时钟输出 6.2.7时钟安全系统(CSS&#xff09; 小结 前言 从…

单链表操作

单链表操作 1. 链表的概念2. 链表的分类2.1.单向或者双向2.2 带头或者不带头2.3 循环或者非循环2.4 常用的链表 3. 单链表的实现3.1 单链表的打印3.2 单链表的头插3.3 单链表的尾插3.4 单链表的头删3.5 单链表的尾删3.6 单链表的查询3.7 在pos前插入数据3.8 在pos后插入数据3.9…

Linux——进程通信(一) 匿名管道

目录 前言 一、进程间通信 二、匿名管道的概念 三、匿名管道的代码实现 四、管道的四种情况 1.管道无数据&#xff0c;读端需等待 2.管道被写满&#xff0c;写端需等待 3.写端关闭&#xff0c;读端一直读取 4.读端关闭&#xff0c;写端一直写入 五、管道的特性 前言 …

不锈钢多功能电工剥线钳分线绕线剪线剥线钳剥线压线扒皮钳子

品牌&#xff1a;银隆 型号&#xff1a;089B绿色 材质&#xff1a;镍铬钢&#xff08;不锈钢&#xff09; 颜色分类&#xff1a;089B灰色,089B红色,089B绿色,089B黑色,089B橙色 功能齐集一身&#xff0c;一钳多用&#xff0c;多功能剥线钳。剥线&#xff0c;剪线&#xff…

Java-CAS 原理与 JUC 原子类

由于 JVM 的 synchronized 重量级锁涉及到操作系统&#xff08;如 Linux&#xff09; 内核态下的互斥锁&#xff08;Mutex&#xff09;的使用&#xff0c; 其线程阻塞和唤醒都涉及到进程在用户态和到内核态频繁切换&#xff0c; 导致重量级锁开销大、性能低。 而 JVM 的 synchr…

免费阅读篇 | 芒果YOLOv8改进114:上采样Dysample:顶会ICCV2023,轻量级图像增采样器,通过学习采样来学习上采样,计算资源需求小

&#x1f4a1;&#x1f680;&#x1f680;&#x1f680;本博客 改进源代码改进 适用于 YOLOv8 按步骤操作运行改进后的代码即可 该专栏完整目录链接&#xff1a; 芒果YOLOv8深度改进教程 &#x1f680;&#x1f680;&#x1f680; DySample是一个超轻量级和有效的动态上采样器…

DDos攻击如何被高防服务器有效防范?

德迅云安全-领先云安全服务与解决方案提供商 什么是DDos攻击&#xff1f; DDos攻击是一种网络攻击手段&#xff0c;旨在通过使目标系统的服务不可用或中断&#xff0c;导致无法正常使用网络服务。DDos攻击可以采取多种方式实施&#xff0c;包括洪水攻击、压力测试、UDP Flood…

HTML静态网页成品作业(HTML+CSS)——游戏战地介绍设计制作(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

关于PXIE3U18槽背板原理拓扑关系

如今IT行业日新月异&#xff0c;飞速发展&#xff0c;随之带来的是数据吞吐量的急剧升高。大数据&#xff0c;大存储将成为未来数据通信的主流&#xff0c;建立快速、大容量的数据传输通道将成为电子系统的关键。随着集成技术和互连技术的发展&#xff0c;新的串口技术&#xf…

【QT+QGIS跨平台编译】之七十七:【QGIS_Gui跨平台编译】—【错误处理:字符串错误】

文章目录 一、字符串错误二、处理方法三、涉及到的文件一、字符串错误 常量中有换行符错误:(也有const char * 到 LPCWSTR 转换的错误) 二、处理方法 需要把对应的文档用记事本打开,另存为 “带有BOM的UTF-8” 三、涉及到的文件 src\gui\qgsadvanceddigitizingdockwidge…