滑动窗口介绍

1.基本概念

利用单调性,使用同向双指针,两个指针之间形成一个窗口

  • 子串与子数组都是连续的一段
  • 子序列时不连续的

2.为什么可以用滑动窗口?

暴力解决时发现两个指针不需要回退(没必要回退,一定不会符合结果)也可以解决当前的问题,此时就可以使用滑动窗口

3.实际应用

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

1.题目要求

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

示例 1:

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

示例 2:

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

2.解题思路

  1. 首先此题是要求得子串,并且是最长的无重复字符,
  2. 最容易想到的就是暴力解法,遍历所有的子串,通过数组模拟哈希表来判断是否重复,此时时间复杂度为o(n2)级别
  3. 但是此时有一个更优解,如果右边的指针遇到了重复值,那么从重复的前一个值开始左边的值都不可能存在比当前长度更长的答案了,此时我们就可以直接从重复的前一个值的下一个字符出发判断,此时right继续往后判断即可,此时时间复杂度为o(n)级别
    滑动窗口介绍_第1张图片

3.解题步骤

  1. left = 0, right = 0
  2. 进窗口,当前字符加入窗口
  3. 判断,若当前字符并未重复,则right++
  4. 出窗口,若字符重复,依次出窗口,left++,直至无重复
  5. 更新结果,当字符成功加入窗口时,就可以更新结果
  6. 结束条件:right指针移动到最右边位置时

4.解题代码:

    public int lengthOfLongestSubstring(String s) {
        int[] hash = new int[128];
        char[] ch = s.toCharArray();
        int ret = 0;
        for (int left = 0, right = 0; right < s.length(); right++) {
            // 进窗口
            hash[ch[right]]++;
            while (hash[ch[right]] > 1) {
                // 出现重复
                //出窗口
                hash[ch[left]]--;
                left++;
            }
            ret = Math.max(ret, right - left + 1);
        }
        return ret;
    }

2.最大连续1的个数|||

1.题目要求

给定一个二进制数组 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。

2.解题思路

其实这道题就是需要我们找到一个最长子串,并且这个字串最多可以包含k个0,我们可以使用一个变量记录当前子串中0的个数,当0的个数超出k个之后,从left指针往后依次出窗口,直到满足条件,right指针接着往后面走

3.解题步骤

滑动窗口介绍_第2张图片

4.解题代码

public int longestOnes(int[] nums, int k) {
        int ret = 0;
        int zCount = 0;
        int left = 0, right = 0;
        while (right < nums.length) {
            if (nums[right] == 0) {
                zCount++;
            }
            while (zCount > k) {
                if (nums[left++] == 0) {
                    zCount--;
                }
            }
            ret = Math.max(ret, right - left+1);
            right++;
        }
        return ret;
    }

3.将X减到0的最小操作数

1.题目要求

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。

示例 1:
输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。

示例 2:
输入:nums = [5,6,7,8,9], x = 4
输出:-1

示例 3:
输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。

提示:
1 <= nums.length <= 105
1 <= nums[i] <= 104
1 <= x <= 109

2.解题思路

  1. 因为题目说明既可以从左边减也可以从右边减,这对我们做题来说是非常困难的,所以我们要想办法,变换一下题目
  2. 既然题目要求我们求减到0的最小操作次数,我们都知道数组的和是不变的,也就是说sum-x也是不变的
  3. 那么我们就可以求和为X的最小操作次数改为求数组内和为sum-X的最长子串,这样nums.length - ret就是我们需要的结果了
  4. 最后别忘了处理特殊值和边界条件即可

3.解题步骤

滑动窗口介绍_第3张图片

4.解题代码

class Solution {
    public int minOperations(int[] nums, int x) {
        int target = 0;
        for (int i = 0; i < nums.length; i++) {
            target+=nums[i];
        }
        target -= x;
        // 处理边界条件
        if (target < 0) return -1;
        int sum = 0;
        int ret = -1;
        for (int left = 0, right = 0; right < nums.length; right++) {
            // 加入结果
            sum+=nums[right];
            // 超出范围
            while (sum > target) {
                // 出结果集
                sum-=nums[left++];  
            }
            // 判断长度,更新结果
            if (sum == target) {
                ret = Math.max(ret, right - left + 1);
            }
        }
        return ret == -1? ret : nums.length - ret;
    }
}

你可能感兴趣的:(滑动窗口,java)