【算法系列】滑动窗口

文章介绍了几个基于滑动窗口和数组处理的算法问题,包括计算长度为k的连续子数组的最大总和、无重复字符的最长子串、最大子数组和、最小覆盖子串以及滑动窗口最大值。每个问题都提供了暴力解法或优化策略,并给出了Java实现。
摘要由CSDN通过智能技术生成

计算长度为k的连续子数组的最大总和

给定一个整数数组,计算长度为k的连续子数组的最大总和。

输入:arr [] = {100,200,300,400} k = 2

输出:700

解释:300 + 400 = 700

解决思路

  1. 暴力解法:从k到n-k+1,计算k长度大小的窗口。
  2. 计算(0,k-1)窗口的数据和,减去第一个,加上最后一个。求最大值。

Java实现

public class SlidingWindow {

    public void maxSumSliding() {
        //定义长度
        int k = 2;
        //定义数组
        int[] arr = {100, 200, 300, 400};
        int size = arr.length;
        if (size < k) {
            return;
        }
        int maxSum = 0;
        for (int i = 0; i < k; i++) {
            maxSum += arr[i];
        }
        for (int i = k; i < size; i++) {
            maxSum = Math.max(maxSum + arr[i] - arr[i - k], maxSum);
        }
        System.out.println(maxSum);
    }
}

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

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

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

解决思路

  1. 发现前面子串中有该元素,需要移除旧的元素。不是第一个,不是全部移除,而是用while判断set.contains(s.charAt(i))

Java实现

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int len = s.length();
        HashSet<Character> set = new HashSet<>();
        int res = 0;
        int start = 0;
        for (int i = 0; i < len; i++) {
            while (set.contains(s.charAt(i))) {
                set.remove(s.charAt(start));
                start++;
            }
            set.add(s.charAt(i));
            res = Math.max(res, i - start + 1);
        }
        return res;
    }
}

53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

解决思路

  1. 求子串的和,判断是否小于0,小于0,则不累加。

Java实现

class Solution_LC53_II {
    public int maxSubArray(int[] nums) {
        int res = Integer.MIN_VALUE;
        int curSum = 0;
        for (int i = 0; i < nums.length; i++) {
            curSum += nums[i];
            res = Math.max(curSum, res);
            if (curSum < 0) {
                curSum = 0;
            }
        }
        return res;
    }
}

76. 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

解决思路

  1. 计算字符串t中每个字符出现的次数
  2. 挨个添加字符串s中的字符,如果该字符数出现在s中的次数小于t,有效次数+1;
  3. 尝试删除最前面的元素,当满足t中不存在或者t中出现的次数小于s,最前面的字符是多余的。
  4. 当有效字符的长度等于t的长度,且截取的字符串长度小于最小值,更新。

Java实现

class Solution {
    public String minWindow(String s, String t) {
        HashMap<Character, Integer> st = new HashMap<>();
        for (int i = 0; i < t.length(); i++) {
            Integer count = st.getOrDefault(t.charAt(i), 0);
            st.put(t.charAt(i), count + 1);
        }
        String ans = "";
        int len = Integer.MAX_VALUE;
        int j = 0;
        int count = 0;//有效字符
        HashMap<Character, Integer> sh = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            sh.put(s.charAt(i), sh.getOrDefault(s.charAt(i), 0) + 1);
            if (st.containsKey(s.charAt(i)) && sh.get(s.charAt(i)) <= st.get(s.charAt(i))) {
                count++;
            }
            while (j < i && (!st.containsKey(s.charAt(j)) || sh.get(s.charAt(j)) > st.get(s.charAt(j)))) {
                Integer cnt = sh.get(s.charAt(j));
                sh.put(s.charAt(j), cnt - 1);
                j++;
            }
            if (i - j + 1 < len && count == t.length()) {
                len = i - j + 1;
                ans = s.substring(j, i + 1);
            }
        }
        return ans;

    }
}

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

解决思路

  1. 优先队列,倒序。最大的在最上面。判断最大的元素的索引是否超过窗口,[i-k+1,i]
  2. 滑动窗口,判断队列首是否超过窗口,挨个将次大的元素排列在队列后面。

Java实现

优先队列

class Solution_LC239_II {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) {
            return new int[0];
        }
        int n = nums.length;
        PriorityQueue<int[]> priorityQueue = new PriorityQueue<>((a, b) -> {
            return b[1] > a[1] ? 1 : -1;
        });
        int[] res = new int[n - k + 1];
        for (int i = 0; i < nums.length; i++) {
            priorityQueue.offer(new int[]{i, nums[i]});
            if (i >= k - 1) {
                while (priorityQueue.peek()[0] <= i - k) {
                    priorityQueue.poll();
                }
                res[i - k + 1] = priorityQueue.peek()[0];
            }
        }
        return res;
    }
}

滑动窗口

class Solution_LC239_II {

    public int[] maxSlidingWindow(int[] nums, int k) {
        Deque<Integer> deque = new LinkedList<>();
        int len = nums.length;
        int[] res = new int[len - k + 1];
        for (int i = 0; i < len; i++) {
            while (!deque.isEmpty() && deque.peek() <= i - k) {
                deque.poll();
            }
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }
            deque.offer(i);
            if (i - k + 1 >= 0) {
                res[i - k + 1] = nums[deque.peek()];
            }
        }
        return res;
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值