剑指 - 滑动窗口两道题目

本文详细介绍了滑动窗口在数组和字符串问题中的应用,通过59题滑动窗口最大值、57题寻找连续子序列和为特定值的解题过程,展示了如何利用双端队列解决这类问题。同时,还提供了58题翻转字符串的解决方案,强调了滑动窗口在有序数组问题中的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

滑动窗口的一般套路是:
维护两个指针,一左一右,代表窗口的两个边界。
左右边界只能向左不能向右。

按要求在右边界填加数组新元素
在一定情况下,移除左边界的元素【即左指针左移】
维护窗口中值保持某种性质

大部分情况下都适用双端队列Deque来实现


59是滑动窗口最经典的题目。 滑动窗口分为固定与变长,这题属于固定窗口。

本题还可以顺便练习以下双端队列Deque的API【以前我一直以为只有LinkedList才有队列,原来还有一个接口】


57题是变长滑动窗口题。

一、59 滑动窗口的最大值

维护一个双端队列,保证输入元素的单调递减。
每次插入一个元素,将最左元素除去

注意:若最左元素是窗口外元素,需要去除掉

 public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length < k) return new int[0];
        // 因为k总是有效的,不必考虑k是否大于范围
        int len = nums.length;
        if (len == 0) return new int[0];
        int[] res = new int[len - k + 1];

        Deque<Integer> deque = new LinkedList<>();
        //将第一趟的结果存入res[0]
        for (int i = 0; i < k; i++) {
            while (!deque.isEmpty() && deque.peekLast() < nums[i]) deque.pollLast();
            deque.offerLast(nums[i]);
        }
        res[0] = deque.peekFirst();
        for (int i = k; i < len; i++) {
            if (deque.peekFirst() == nums[i - k]) deque.pollFirst();
            while (!deque.isEmpty() && deque.peekLast() < nums[i]) deque.pollLast();
            deque.offerLast(nums[i]);
            //注意原数组与结果数组的下标对应关系
            res[i - k + 1] = deque.peekFirst();
        }

        return res;
    }

二、57 连续子序列和为某一个值

维护左右指针,和一个sum,若窗口值总和sum < target, 右指针右移,直到sum >= target 若sum > target ,将左指针右移,并不断减少sum的对应值 若sum == target, 判断队长是不是1,将结果添加进入【**并将最左元素移除**】
 public int[][] findContinuousSequence(int target) {
        if(target == 1) return new int[0][0];
        
        List<List<Integer>> res = new ArrayList<>();

        for (int i = 1; i < target / 2 + 1; i ++) {
            int sum = 0;
            for (int j = i; j < target / 2 + 2; j++ ) {
                sum += j;
                if (sum == target) {
                    List<Integer> list = new ArrayList<>();
                    for (int k = i; k <= j; k++) {
                        list.add(k);
                    } 
                    res.add(list);
                }
                if (sum > target) break;
            }
        }
        int[][] result = new int[res.size()][];
        for (int i = 0; i < res.size(); i++) {
           result[i] = res.get(i).stream().mapToInt(Integer::valueOf).toArray();
        }

        return result;

    }

三、58 翻转字符串

从右向左定义一个窗口,使得其中的每个字符不为空

这题的关键在于使用substring()

 public String reverseWords(String s) {
        if (s == null || s.trim().isEmpty()) return "";
        s = s.trim();
        int hi = s.length() - 1, lo = hi;
        StringBuilder sb = new StringBuilder();
        List<String> res = new ArrayList<>();
        while (hi >= 0) {
            while (s.charAt(hi) == ' ') hi--;
            lo = hi;
           while (lo >= 0 && s.charAt(lo) != ' ') lo--;
           sb.append(s.substring(lo + 1, hi + 1) + " ");
            hi = lo;
        }
        return sb.toString().trim();
    }

总结

有序数组需要条件反射式的想到三种解法:
(1)二分;
(2)左右双指针;
(3)滑动窗口。

有“最大值”不一定是动态规划嗷!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值