[LC优选算法#2] 滑动窗口 | 长度最小的子数组 | 无重复字符的最长子串 | 最大连续1的个数

1. 算法思想

滑动窗口的本质是:维护一个满足条件的连续子数组/子串,通过移动左右边界来"滑动"这个窗口,从而找到最优解。 滑动窗口是更加严格的双指针算法,大致思路都是用两个不回退的指针维护窗口。而且滑动窗口仅支持元素为正数的情况,不适用于负数或0(出窗口的时机无法确定)。

基本步骤: 进窗口 -> 判断条件 -> 出窗口 -> 更新结果

(更新结果的时机因题而异)

2. 经典例题

2.1 ⻓度最⼩的⼦数组

⻓度最⼩的⼦数组

解题思路:

  1. 暴力解法 O(N^2):从数组中的每个元素开始向后枚举,找出满足条件的最小长度。

显然,暴力解法中存在很多重复 的计算。

例如下图,如果按照暴力枚举的方式统计,当2枚举结束后需要从3开始重新计算长度,但3之后的12都是没必要重新计数的。根据这一点,我们可以试着维护一个"窗口"来解决重复统计的耗时问题。

  1. 滑动窗口 O(N):这个窗口满足内部所有元素之和小于 target的条件。如果窗口内元素之和大于等于target,则更新结果,然后在逐个排除左边元素的同时,继续判断是否符合条件并继续更新结果;如果元素之和不满足条件,则加入新元素。

时间复杂度 :由于维护窗口的left和right指针都是不回退 的,最坏情况是两者分别遍历一次数组,因此总共耗时<= 2*N,时间复杂度为O(N)

cpp 复制代码
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums)
    {
        int n = nums.size();
        int len = INT_MAX;
        int sum = 0;
        for(int left=0,right=0; right < n; right++)
        {
            sum += nums[right];
            while(sum >= target)
            {
                len = min(len, right-left+1);
                sum -= nums[left];
                left++;
            }
        }

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

2.2 ⽆重复字符的最⻓⼦串

⽆重复字符的最⻓⼦串

解题思路:

  1. 暴力解法+哈希表 O(N^2):从数组中的每个元素开始向后枚举,找出满足条件的最大长度。用哈希表统计出字符出现的频次,来判断什么时候子串出现了重复元素。

显然,研究对象依旧是一段连续的区间,以下图为例,在枚举完d后,没有必要以ea为起点重新向后枚举,因为重复元素a仍然在橙色区间中,枚举的长度只会更短。因此我们可以通过向右缩小窗口直至满足条件(绿色区间),然后继续让新元素进窗口。

2.滑动窗口+哈希表 O(N) :右端元素进⼊窗⼝的时候,哈希表统计这个字符的频次:

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

时间复杂度 :由于维护窗口的left和right指针都是不回退 的,最坏情况是两者分别遍历一次数组,因此总共耗时<= 2*N,时间复杂度为O(N)

cpp 复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s)
    {
        int left = 0;
        int right = 0;
        int n = s.size();
        int maxlen = 0;
        vector<int> hash(128, 0); //128个字符,用数组模拟哈希表

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

            while(hash[s[right]] == 2) //加1在前判断在后则为2
            {
                hash[s[left++]]--;
            }

            maxlen = max(maxlen, right - left + 1);
            right++;
        }

        return maxlen;
    }
};

2.3 最⼤连续 1 的个数 III

最⼤连续 1 的个数 III

解题思路:

可以翻转最多 k 个 0 ,转化为找一个包含 k 个 0 的最长区间。

  1. 暴力解法O(N^2):以每个元素为起点,依次向后找最长的子数组。

题目包含连续区间,考虑用滑动窗口思想解决:

  1. 滑动窗口O(N):窗口内部存放有k个0的连续子串,以子串中0的个数为判断条件,维护窗口,超出k个0则停止更新,left指针左移;不够k个0则right指针右移,更新窗口。
cpp 复制代码
class Solution {
public:
    int longestOnes(vector<int>& nums, int k)
    {
        int left = 0;
        int right = 0;
        int zerocnt = 0;
        int len = 0;
        int n = nums.size();

        while(right < n)
        {
            if(nums[right] == 0)
            {
                zerocnt++;
            }

            while(zerocnt > k)
            {
                if(nums[left++] == 0)
                {
                    zero--;
                }
            }

            len = max(len, right - left + 1);
            right++;
        }

        return len;
    }
};

// 本期内容就到这里,如果对你有帮助,请三连支持!我是青云,我们下期见^_~

相关推荐
AI小老六9 小时前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术10 小时前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize11 小时前
初识DFS 与 BFS:递归、队列与图遍历
算法
罗西的思考1 天前
机器人 / 强化学习】HIL-SERL:人类在环驱动的具身智能进化框架
人工智能·算法·机器学习
美团技术团队1 天前
LongCat 开源 VitaBench 2.0:长期动态智能体基准新标杆
人工智能·算法
To_OC2 天前
LC 207 课程表:刚学图论那会儿,我连这是拓扑排序都没看出来
javascript·算法·leetcode
To_OC2 天前
LC 208 实现 Trie 前缀树:曾被名字劝退,写完发现是送分题
javascript·算法·leetcode
BadBadBad__AK2 天前
线段树维护区间 k 次方和
c++·数学·算法·stl
_清歌2 天前
DSpark 深度解读:DeepSeek-V4 如何用「半自回归」把推理速度提升 85%
算法