【数据结构与算法】滑动窗口

本文介绍了一种称为滑动窗口的算法,该算法通过记录窗口的状态,在数组中找到符合条件的元素,特别适用于寻找窗口内最大值的问题。文章提供了一个具体的例子,并给出了完整的Java代码实现。

滑动窗口

  1. 可以理解为一个可以容纳n个元素的窗口,通过每次记录窗口的状态,找到符合条件的窗口或者说在窗口中得到符合条件的元素。
  2. 通过滑动窗口可以减少时间复杂度

【题目】有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次 向右边滑一个位置。
例如, 数组为[4,3,5,4,3,3,6,7], 窗口大小为3时:

窗口滑动过程窗口中的最大值
[ 4 3 5 ] 4 3 3 6 75
4 [3 5 4] 3 3 6 75
4 3 [ 5 4 3] 3 6 75
4 3 5 [ 4 3 3 ] 6 74
4 3 5 4 [3 3 6 ] 76
4 3 5 4 3 [ 3 6 7]7

如果数组长度为n, 窗口大小为w, 则一共产生n-w+1个窗口的最大值。
请实现一个函数。 输入:整型数组arr, 窗口大小为w。
输出:一个长度为n-w+1的数组res, res[i]表示每一种窗口状态下的 以本题为例, 结果应该返回{5,5,5,4,6,7}。

【代码实现】

import java.util.Arrays;
import java.util.LinkedList;

public class SildingWindow {
    public static void main(String[] args) {
        int[] arr={4,3,5,4,3,3,6,7};
        int[] res = slidingWindow(arr, 3);
        System.out.println(Arrays.toString(res));
    }

    /*如果数组长度为n, 窗口大小为w, 则一共产生n-w+1个窗口的最大值。
请实现一个函数。 输入:整型数组arr, 窗口大小为w。
输出:一个长度为n-w+1的数组res, res[i]表示每一种窗口状态下的 以本题为例, 结果应该
返回{5,5,5,4,6,7}。*/
    public static int[] slidingWindow(int[] arr, int w) {
        //1.定义一个双端队列,存放的是arr数组的下标
        LinkedList<Integer> deque = new LinkedList<>();
        //2.定义一个数组用来保存窗口最大值
        int[] res = new int[arr.length - w +1];
        int index=0;  //辅助变量给res使用
        for (int i = 0; i < arr.length; i++) {  //3.从左到右移动窗口

            while (!deque.isEmpty() && arr[deque.peekLast()] <= arr[i]) { // 4.判断添加的时候是否需要将deque中last元素弹出
                //当deque中最后一个值对应的arr的值小于或者等于ar[i]的时候,要将deque最后的值弹出直到arr[last]的值大于arr[i]或者deque为空
                deque.pollLast();
            }
            deque.addLast(i);  //将当前位置的索引加入到队列中
            if(deque.peekFirst()==i-w){
                deque.pollFirst();
            }
            if (i >=w - 1) {  //由于i要至少到大w-1位置这个时候窗口才是满的 刚开始窗口中的数的个数为1,2...w
                res[index++]=arr[deque.peekFirst()];    //5.将窗口最大值加入数组
            }
        }
        return res;
    }
}

【总结】

  • 每个位置的数最多进窗口一次出窗口一次,弹出过的数不会找回

  • 如果滑过的数字为n,双端队列更新的总代价时间复杂度O(N)

  • 双端队列滑过n次,双端队列更新的平均代价时间复杂度O(1)

### Python 中滑动窗口算法的实现及应用 滑动窗口是一种高效算法设计模式,主要用于处理数组或字符串上的子区间问题。它能够将原本需要双重循环遍历的问题优化为单层循环,从而显著降低时间复杂度[^4]。 #### 1. 基本原理 滑动窗口的核心思想是利用双指针维护一个动态变化的窗口区域,在满足特定条件下调整窗口大小并记录最优解。通常情况下,右边界负责扩展窗口,左边界则收缩窗口以保持约束条件成立[^2]。 --- #### 2. 经典问题:无重复字符的最长子串 这是一个典型的滑动窗口应用场景。目标是从给定字符串中找到不包含任何重复字符的最大长度子串。 ##### 实现代码 ```python def length_of_longest_substring(s: str) -> int: char_index_map = {} # 储字符最近一次出现的位置 max_length = 0 # 记录当前最大长度 start = 0 # 左边界的起始位置 for end, char in enumerate(s): # 遍历字符串,end表示右边界的当前位置 if char in char_index_map and char_index_map[char] >= start: # 如果发现重复字符,则更新左边界的起点 start = char_index_map[char] + 1 # 更新字符最新索引位置 char_index_map[char] = end # 当前窗口长度 current_window_length = end - start + 1 max_length = max(max_length, current_window_length) return max_length ``` 上述代码的时间复杂度为 O(n),其中 n 是输入字符串的长度。这是因为每个字符最多被访问两次(一次由 `start` 指针控制,另一次由 `end` 指针控制)[^1]。 --- #### 3. 另一实例:求和等于 k 的最小子数组长度 此问题是另一种形式的滑动窗口应用,目的是在一个整数数组中找出其和恰好等于指定值 k 的最小连续子数组长度。 ##### 实现代码 ```python def min_subarray_len(nums: list[int], target_sum: int) -> int: min_length = float('inf') # 初始化为无穷大 window_sum = 0 # 窗口内的总和 start = 0 # 左边界初始位置 for end in range(len(nums)): # 遍历整个数组 window_sum += nums[end] # 扩展窗口右侧 while window_sum >= target_sum: # 收缩左侧直到不再符合条件 min_length = min(min_length, end - start + 1) window_sum -= nums[start] start += 1 return min_length if min_length != float('inf') else 0 ``` 这段代码同样基于滑动窗口的思想,通过不断调整左右边界来寻找满足条件的最小区间。 --- #### 4. 使用场景拓展 除了以上两种经典例子外,滑动窗口还可以应用于其他领域: - **机器学习中的特征提取** 当面对高维时序数据时,可以通过定义固定宽度的滑动窗口截取片段作为模型输入[^5]。 - **实时流数据分析** 例如监控系统性能指标或者金融交易信号检测等场合下都需要快速统计一段时间范围内的汇总信息。 --- #### 5. 性能分析 相比暴力枚举所有可能组合的方式,滑动窗口极大地减少了不必要的计算量。然而需要注意的是,并不是所有的题目都适合直接套用标准模板;有时还需要结合具体需求做出适当修改才能达到最佳效果。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值