[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;
    }
};

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

相关推荐
小欣加油2 小时前
leetcode3689最大子数组总值I
c++·算法·leetcode·职场和发展·贪心算法
下午写HelloWorld2 小时前
【概念与应用】轻量级加密算法LEA、动态脱敏算法DDA、零知识证明ZKP和优化协同交互协议OCIP
算法·区块链·密码学·安全架构·零知识证明
飞舞哲2 小时前
三维点云最小二乘拟合MATLAB程序
开发语言·算法·matlab
Coder-magician2 小时前
《代码随想录》刷题打卡day12:二叉树part02
数据结构·c++·算法
海梨花2 小时前
字节面试高频算法题
java·算法·面试·职场和发展
aqiu1111113 小时前
python02
算法
瓦特what?3 小时前
位运算核心技巧与应用
java·jvm·算法
无限码力3 小时前
阿里算法岗 0530笔试真题 - 荆棘林的最优砍断计划
算法·阿里笔试真题·阿里机试真题·阿里算法岗笔试真题·阿里巴巴笔试真题
随意起个昵称3 小时前
线性dp-LIS题目5(导弹拦截,二分优化)
c++·算法·动态规划