《算法题讲解指南:优选算法-滑动窗口》--09长度最小的子数串,10无重复字符的最长字串

🔥小叶-duck个人主页

❄️个人专栏《Data-Structure-Learning》

《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--从优选到贪心

未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游


目录

09.长度最小的子数串

题目链接:

题目描述:

题目示例:

解法一:(暴力求解)(会超时)

算法思路:

解法二:(滑动窗口)

算法思路:

C++代码演示:

算法总结及流程解析:

10.无重复字符的最长字串

题目链接:

题目描述:

​编辑

题目示例:

解法一:(暴力求解)(不会超时,可以通过)

算法思路:

解法二:(滑动窗口)

算法思路:

C++代码演示:

算法总结及流程解析:

结束语


09.长度最小的子数串

题目链接:

209. 长度最小的子数组 - 力扣(LeetCode)

题目描述:

题目示例:

解法一:(暴力求解)(会超时)

算法思路:

【从前往后】枚举数组中的任意一个元素,把它当成起始位置。然后从这个【起始位置】开始,然后寻找一段最短的区间,使得这段区间的和【大于等于】目标值。

将所有元素作为起始位置所得的结果中,找到【最小值】即可。

解法二:(滑动窗口)

算法思路:

由于此问题分析的对象是【一段连续的区间 】,因此可以考虑【滑动窗口】的思想来解决这道题。

让滑动窗口满足:从 i 位置开始,窗口内所有的和小于 target (那么当窗口内元素之和第一次大于等于目标值的时候,就是 i位置开始,满足条件的最小长度)。

做法:将右端元素划入窗口之中,统计出此时窗口内元素的和:

  • 如果窗口内元素之和大于 target:更新结果,并且将左端元素划出去的同时继续判断是否满足条件并更新结果(因为左端元素可能很小,划出去之后依旧满足条件)
  • 如果窗口内元素之和不满足条件:right++,另下一个元素进入窗口。

为何滑动窗口可以解决问题,并且时间复杂度更低?

这个窗口寻找的是:以当前窗口最左侧元素(记为left1 )为基准,符合条件的情况。也就是在这道题中,从 left1 开始,满足区间和 sum >= target 时的最右侧(记为 right1)能到哪里。

我们既然已经找到从 left1 开始的最优的区间,那么就可以大胆舍去left1 。但是如果继续像方法一一样,重新开始统计第二个元素(left2)往后的和,势必会有大量重复的计算(因为我们在求第一段区间的时候,已经算出很多元素的和了,这些和是可以在计算下次区间和的时候用上的)。

此时**,right1** 的作用就体现出来了,我们只需要将 left1 这个值从 sum 中剔除。从 right1 这个元素开始,往后找满足 left2 元素的区间 (此时 right1 也有可能是满足的,因为 left1 可能很小。sum 剔除掉 left1 之后,依旧满足大于等于 target)。这样我们就能省掉大量重复的计算。

时间复杂度:虽然代码是两层循环,但是我们的 left 指针和 right 指针都是不回退 的,两者最多都往后移动 n 次。因此时间复杂度是 O(N)

C++代码演示:

cpp 复制代码
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) 
    {
        // int left = 0;
        // int right = -1;
        // //right初始化为-1原因是target为正整数,一开始sum一定小于target
        // //所以为了让right++并且能让sum能加上开头元素,就需要初始化为-1
        // int len = 0;
        // int n = nums.size();
        // int min_len = n + 1; //先将最小长度置为非正常值,如果最后没有发生变化则说明没有符合条件的子数组
        // int sum = 0;
        // while (right < n)
        // {
        //     if (sum >= target)
        //     {
        //         len = right - left + 1;
        //         if (min_len > len)
        //         {
        //             min_len = len;
        //         }
        //         sum -= nums[left];
        //         left++;
        //     }
        //     else
        //     {
        //         right++;
        //         if (right < n)
        //         {
        //             sum += nums[right];
        //         }
        //         else
        //         {
        //             break;
        //         }
        //     }
        // }
        // if(min_len == n + 1)
        // {
        //     return 0;
        // }
        // return min_len;

        //代码优化:
        int n = nums.size();
        int sum = 0;
        int len = n + 1;
        int left = 0;
        int right = 0;
        for(right = 0; right < n; right++)
        {
            sum += nums[right];//进窗口
            while(sum >= target)//判断
            {
                len = min(len, right - left + 1);//更新结果
                sum -= nums[left++];//出窗口
            }
        }
        return len == n + 1 ? 0 : len;
    }
};

算法总结及流程解析:

10.无重复字符的最长字串

题目链接:

3. 无重复字符的最长子串 - 力扣(LeetCode)

题目描述:

题目示例:

解法一:(暴力求解)(不会超时,可以通过)

算法思路:

枚举【从每一个位置】开始往后,无重复字符的子串可以到达什么位置。找到其中长度最大的即可。

在往后寻找无重复子串能到达的位置时,可以利用【哈希表统计出字符出现的频次,来判断什么时候子串出现了重复元素。

解法二:(滑动窗口)

算法思路:

研究的对象依旧是一段连续的区间 ,因此继续使用【滑动窗口】思想来优化。

让滑动窗口满足:窗口内所有元素都是不重复的。

做法:右端元素 s[ right ] 进入窗口的时候,哈希表统计这个字符的频次:

  • 如果这个字符出现的频次超过 1 ,说明窗口内有重复元素 ,那么就从左侧开始划出窗口 ,直到 s[ right ] 这个元素的频次变为1,然后再更新结果。
  • 如果没有超过 1,说明当前窗口没有重复元素,可以直接更新结果

C++代码演示:

cpp 复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) 
    {
        int len = 0;
        int left = 0;
        int right = 0;
        int hash[128] = { 0 }; //使用数组来模拟哈希表
        while(right < s.size())
        {
            hash[s[right]]++;//进入窗口
            while(hash[s[right]] > 1)//判断
            {
                hash[s[left++]]--;//出窗口
            }
            len = max(len, right - left + 1);//更新结果
            right++;
        }
        return len;
    }
};

算法总结及流程解析:

结束语

到此,09长度最小的子数串,10无重复字符的最长字串 两道算法题就讲解完了。**第一题,通过暴力解法和滑动窗口对比,详细解释了滑动窗口的高效原理,即通过左右指针不回退降低时间复杂度。第二题同样采用滑动窗口,利用哈希表统计字符频次来检测重复。**希望大家能有所收获!

相关推荐
白太岁2 小时前
Muduo:(5) 主 Reactor 之 Acceptor 与 SubReactor 的分发
服务器·网络·c++·网络协议·tcp/ip
Frostnova丶2 小时前
LeetCode 762 二进制表示中质数个计算置位
算法·leetcode
WZ188104638692 小时前
LeetCode第367题
算法·leetcode
季明洵2 小时前
Java实现循环队列、栈实现队列、队列实现栈
java·数据结构·算法··队列
Non importa2 小时前
二分法:算法新手第三道坎
c语言·c++·笔记·qt·学习·算法·leetcode
王老师青少年编程2 小时前
2020年信奥赛C++提高组csp-s初赛真题及答案解析(完善程序第1题)
c++·题解·真题·初赛·信奥赛·csp-s·提高组
WZ188104638692 小时前
LeetCode第2368题
算法·leetcode
iAkuya2 小时前
(leetcode)力扣100 74 数组中的第K个最大元素(快速选择\堆)
数据结构·算法·leetcode
学编程的闹钟2 小时前
安装GmSSL3库后用VS编译CMake源码
c语言·c++·ide·开发工具·cmake·visual studio