Day8--滑动窗口与双指针--1004. 最大连续1的个数 III,1658. 将 x 减到 0 的最小操作数,3641. 最长半重复子数组

Day8--滑动窗口与双指针--1004. 最大连续1的个数 III,1658. 将 x 减到 0 的最小操作数,3641. 最长半重复子数组

今天要训练的题目类型是:【不定长滑动窗口】,题单来自@灵艾山茶府

滑动窗口相当于在维护一个队列 。右指针的移动可以视作入队 ,左指针的移动可以视作出队

不定长滑动窗口主要分为三类:求最长子数组,求最短子数组,求子数组个数。

今天的题目类型是:求最长子数组。

1004. 最大连续1的个数 III

思路【我】:

题意:窗口中最多有k个0

不定长滑动窗口三步曲:入--出--更新

java 复制代码
class Solution {
    public int longestOnes(int[] nums, int k) {
        // 题意:窗口中最多有k个0
        int n = nums.length;
        int zero = 0;
        int left = 0;
        int maxLen = 0;
        for (int i = 0; i < n; i++) {
            // 1,入
            if (nums[i] == 0) {
                zero++;
            }
            // 2,出
            while (zero > k) {
                if (nums[left] == 0) {
                    zero--;
                }
                left++;
            }
            // 3,更新
            maxLen = Math.max(maxLen, i - left + 1);
        }
        return maxLen;
    }
}

附加题:顺便把这两题也做了吧,几乎是复制过去就能提交。487. 最大连续1的个数 II485. 最大连续 1 的个数

1658. 将 x 减到 0 的最小操作数

思路【灵艾山茶府】:

逆向思维:要选头尾的元素相加等于x,也就是中间的元素相加等于total-x。

要使头尾相加的元素最少,也就是中间的窗口尽量长------也就是求多大的窗口可以累计和为total-x

  1. 使用Arrays.stream(nums).sum()求和,记为total
    • 如果x比total还大,没办法,返回-1
    • 要求的total-x定义为need。如果need==0,答案就是要把整个数组都删掉(这里要单独讨论,否则need为0,就求不到窗口了)
  2. 不定长滑动窗口三步曲:入--出--更新
    1. 更新(注意不能一找到need==sum就返回,要找最大的窗口)
  3. 如果maxLen有值(不再为0),也就是中间窗口能凑成need,就是有恰好。否则就是凑不成,返回-1。
java 复制代码
class Solution {
    public int minOperations(int[] nums, int x) {
        int n = nums.length;
        // 思路:逆向思维:要选头尾的元素相加等于x,也就是中间的元素相加等于total-x
        // 要使头尾相加的元素最少,也就是中间的窗口尽量长------也就是求多大的窗口可以累计和为total-x

        // 使用Arrays.stream(nums).sum()求和
        int total = Arrays.stream(nums).sum();

        // 如果x比total还大,没办法,返回-1
        if (x > total) {
            return -1;
        }

        // 要求的total-x定义为need
        int need = total - x;
        // 如果need==0,答案就是要把整个数组都删掉(这里要单独讨论,否则need为0,就求不到窗口了)
        if (need == 0) {
            return n;
        }

        // 初始化
        int left = 0;
        int maxLen = 0;
        int sum = 0;

        // 开始滑动窗口
        for (int i = 0; i < n; i++) {
            // 1,入
            sum += nums[i];
            // 2,出
            while (left < n && sum > need) {
                sum -= nums[left];
                left++;
            }
            // 3,更新(注意不能一找到need==sum就返回,要找最大的窗口)
            if (sum == need) {
                maxLen = Math.max(maxLen, i - left + 1);
            }
        }
        // 如果maxLen有值,也就是中间窗口能凑成need,就是有恰好。否则就是凑不成,返回-1
        return maxLen != 0 ? n - maxLen : -1;
    }
}

简洁版:

java 复制代码
class Solution {
    public int minOperations(int[] nums, int x) {
        int n = nums.length;
        int total = Arrays.stream(nums).sum();

        if (x > total) {
            return -1;
        }

        int need = total - x;
        if (need == 0) {
            return n;
        }

        int left = 0;
        int maxLen = 0;
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += nums[i];
            while (left < n && sum > need) {
                sum -= nums[left];
                left++;
            }
            if (sum == need) {
                maxLen = Math.max(maxLen, i - left + 1);
            }
        }
        return maxLen != 0 ? n - maxLen : -1;
    }
}

3641. 最长半重复子数组

思路【我】:

  1. 利用map记录窗口内各个元素出现的次数。

  2. 利用set<元素>,记录窗口内,出现次数>1,也就是"重复"的元素

  3. 不定长滑动窗口三步曲:入--出--更新。

    1. 入。如果同一个元素数量大于1(重复),加入到set
    2. 出。如果set的元素,大于k个,不符合窗口要求,收缩窗口直到窗口合法
      • 减到为0的不用管,只需要关注原来大于1(重复)的元素,现在减到1了,就是不重复了
    3. 更新maxLen
java 复制代码
class Solution {
    public int longestSubarray(int[] nums, int k) {
        int n = nums.length;
        int left = 0;
        int maxLen = 0;
        int[] map = new int[100001];
        Set<Integer> set = new HashSet<>();

        // 开始滑动窗口
        for (int i = 0; i < n; i++) {
            // 1,入。如果同一个元素数量大于1(重复),加入到set
            if (++map[nums[i]] > 1) {
                set.add(nums[i]);
            }
            // 2,出。如果set的元素,大于k个,不符合窗口要求,收缩窗口直到窗口合法
            while (set.size() > k) {
                // 减到为0的不用管,只需要关注原来大于1(重复)的元素,现在减到1了,就是不重复了
                if (--map[nums[left]] == 1) {
                    set.remove(nums[left]);
                }
                left++;
            }
            // 3,更新
            maxLen = Math.max(maxLen, i - left + 1);
        }
        return maxLen;
    }
}

使用map<元素,出现次数>的版本:

java 复制代码
class Solution {
    public int longestSubarray(int[] nums, int k) {
        int n = nums.length;
        int left = 0;
        int maxLen = 0;
        Map<Integer, Integer> map = new HashMap<>();
        Set<Integer> set = new HashSet<>();
        for (int i = 0; i < n; i++) {
            // merge(1)会返回操作后,该key对应的value
            if (map.merge(nums[i], 1, Integer::sum) > 1) {
                set.add(nums[i]);
            }
            while (set.size() > k) {
                if (map.merge(nums[left], -1, Integer::sum) == 1) {
                    set.remove(nums[left]);
                }
                left++;
            }
            maxLen = Math.max(maxLen, i - left + 1);
        }
        return maxLen;
    }
}
相关推荐
小欣加油5 小时前
leetcode56 合并区间
c++·算法·leetcode·职场和发展
lqqjuly5 小时前
前沿算法深度解析(二)
人工智能·算法·机器学习
徐小夕6 小时前
万字长文!千万级文档 RAG 知识库系统落地实践
前端·算法·github
akunkuntaimei7 小时前
2026年高考数学各省真题及答案(完整版)
算法·高考
Hello:CodeWorld7 小时前
C 风格变参 vs C++ 变参模板:核心区别与选型指南
c语言·c++·算法
8Qi89 小时前
LeetCode 516:最长回文子序列
算法·leetcode·职场和发展·动态规划
youngerwang10 小时前
【从搬运工到协处理器:网卡芯片架构、算法、验证与边缘演进深度剖析】
网络·算法·架构·芯片
想要成为糕糕手10 小时前
前端必修课:JavaScript 数组与数据结构底层逻辑全解析
javascript·数据结构·面试
KaMeidebaby10 小时前
卡梅德生物技术快报|纯化重组蛋白实操详解
人工智能·python·tcp/ip·算法·机器学习
手写码匠11 小时前
从零实现 Prompt 工程引擎:结构化提示、自动优化与多轮自省体系
人工智能·深度学习·算法·aigc