经典算法之——滑动窗口

简介: 经典算法之——滑动窗口

image.png


前言


滑动窗口算法是较为入门题目的经典算法之一,一般是一些有规律数组问题的最优解,如果一个数组的问题可以用动态规划解,但又可以使用滑动窗口解决,那么往往滑动窗口的效率更高。

而关于双指针的算法中,它的左右指针则是形成了一个窗口

双指针也并不局限在数组问题,像链表场景的 “快慢指针” 也属于双指针的场景,其快慢指针滑动过程中本身就会产生一个窗口,而这样的话就很容易形成一种变化的滑动窗口,当然也会有定向窗口大小。

而在计算机网路中则存在滑动窗口协议:该协议是 TCP协议 的一种应用,用于网络数据传输时的流量控制,以避免拥塞的发生。该协议允许发送方在停止并等待确认前发送多个数据分组。由于发送方不必每发一个分组就停下来等待确认。因此该协议可以加速数据的传输,提高网络吞吐量。

滑动窗口算法其实和这个是一样的,只是用的地方场景不一样,所以掌握这种算法的技巧在编程中也是很重要的。


基本思路


(1)初始化窗口:

初始化左右边界 left = right = 0,把索引闭区间 [left, right] 称为一个「窗口」。

(2)寻找可行解:

我们先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的满足可行解。

(3)优化可行解:

此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的可行解不再符合要求。同时,每次增加 left,我们都要更新一轮结果。

(4)滑动窗口,直至一次遍历结束:

重复第 2 和第 3 步,直到 right 到达到的尽头。


基本模板


public void slideWindowTemplate(String nums){
    int l = 0, r = 0;        //[初始化窗口]
    //codes...               [其他初始化信息,定义一些维护数据的数据结构]
    while(r < nums.length){ //右边框移动
        r++;                 //[增大窗口]
        //codes.....         [更新窗口中的数据] 
        while(l < r && check(xxx) == false){   //[窗口不满足某种性质]
              l++;             //[缩小窗口]
            //codes...       [维护窗口中的数据]
        }
    }
}

到这里就以几道题目作为引导说明:


例题


一、 定窗口滑动


返回窗口大小为3且平均数大于等于子数组数目5

说明:

i 的范围只能从下标0到(array.lenght-1)-k,而j的范围可以从0到array.lenght-1,就这样形成一个为k的大小窗口,左为i,右为j=i+3


013b633fe49f4346917ba73547355fdb.png

 static int sum;
    static int res;
    public static void main(String[] args) {
        int[] array={11,13,17,23,29,31,7,5,2,3};
        sum=0;res=0;
        numOf(array,3,5);
        System.out.println(res);
    }
    private static void numOf(int[] array,int k,int threshold){
         for(int i=0;i<= array.length-k;i++){//确定左边界
             for(int j=i;j<i+k;j++){ //在这个窗口的大小从左边界向右移动k格
                 sum+=array[j];
             }
             if(sum>=threshold*k) ++res;
             sum=0;
         }
    }


二、变化窗口


就拿变化滑动窗口来说明一下其实滑动窗口问题还有其他的解法

例题:和满足SUM且长度最小的子数组并返回大小


方法一:暴力枚举


说明:

先定义L=0,然后判断是否大于了SUM,如果大于就返回;没有的话就从第二个数据相加并至后一直判断,完成L=0,r遍历完成之后,然后L向右移动一个,继续r=L+1开始继续遍历;当L到最后一个格子的时候就完成任务了。Math.min(min, r-l + 1)用来找出窗口最小又大于SUM值的

ebd15c94732f4b54918915deffff3931.png

    public int minSubArrayLen(int SUM, int[] nums) {
        int min = Integer.MAX_VALUE; //先定义最小为整形的最大值
        for (int l = 0; l < nums.length; l++){
            int sum = nums[i];
            if (sum >= SUM)
                return 1;
            for (int r = l + 1; r < nums.length; r++) {
                sum += nums[r];
                if (sum >= SUM) {
                    min = Math.min(min, r-l + 1);//r-l + 1 为右边框-左边框=窗口的大小
                    break;
                }
            }
        }
        return min == Integer.MAX_VALUE ? 0 : min;
    }


方法二:队列–双指针法


说明:

利用队列的思路,用两个指针的指向头和尾;先把全部的数据进行加入到队列中,然后再根据条件删减前面数据。

ecd5514c219f401f883c89922f5f2369.png

public int minSubArrayLen(int target, int[] nums){
int lo = 0, hi = 0, sum = 0, min = Integer.MAX_VALUE;
        while(hi < nums.length){
            sum+=nums[hi++];
            while(sum >= target){ //可能我们所加最后一个数过大,前面的数小了,可以减去前面
               min = Math.min(min, hi - lo); //hi - lo 为右边框-左边框=窗口的大小
                sum -= nums[lo++];
            }
        }
        return min == Integer.MAX_VALUE ? 0 : min;
}


方法三、二分窗口


说明:

利用二分的思想,先将窗口进行check(),如果满足>=SUM的话需要进行缩小窗口,大于的话就进行扩大


   public int minSubArrayLen(int s, int[] nums) {
        int lo = 0, hi = nums.length-1, min = 0;
        while (lo <= hi) {
            int mid = lo+((hi-lo) >> 1); // 二分窗口
            if (windowExist(mid, nums, s)) { //根据是否需要扩大或者缩小窗口
                hi = mid - 1;//找到就缩小窗口的大小
                min = mid;//如果找到就记录下来
            } else
                lo = mid + 1;//没找到就扩大窗口的大小
        }
        return min;
    }
    //size窗口的大小
    //这个大小窗口的数和无法满足>=s返回FALSE,或者可以满足则返回ture
    private boolean windowExist(int size, int[] nums, int target){ 
        int sum = 0;
        for(int i = 0; i < nums.length; i++){
            if (i >= size){//如果加到超过了该位置的窗口大小还没有满足条件,就进行向右移动
              sum-=nums[i - size]; 
            }
            sum += nums[i];
            if(sum >= target)
                return true;
        }
        return false;
    }

ok~ 滑动窗口先讲到这里,后续有什么补充我再进行添加

目录
相关文章
|
算法
【算法】滑动窗口——最大连续1的个数
【算法】滑动窗口——最大连续1的个数
166 0
|
1月前
|
存储 算法
算法入门:专题二---滑动窗口(长度最小的子数组)类型题目攻克!
给定一个正整数数组和目标值target,找出总和大于等于target的最短连续子数组长度。利用滑动窗口(双指针)优化,维护窗口内元素和,通过单调性避免重复枚举,时间复杂度O(n)。当窗口和满足条件时收缩左边界,更新最小长度,最终返回结果。
|
7月前
|
机器学习/深度学习 监控 算法
员工上网行为监控软件中基于滑动窗口的C#流量统计算法解析​
在数字化办公环境中,员工上网行为监控软件需要高效处理海量网络请求数据,同时实时识别异常行为(如高频访问非工作网站)。传统的时间序列统计方法因计算复杂度过高,难以满足低延迟需求。本文将介绍一种基于滑动窗口的C#统计算法,通过动态时间窗口管理,实现高效的行为模式分析与流量计数。
196 2
|
5月前
|
存储 机器学习/深度学习 监控
公司电脑上网监控中滑动窗口算法的理论构建与工程实现
本文提出一种基于滑动窗口算法的实时网络流量监控框架,旨在强化企业信息安全防护体系。系统采用分层架构设计,包含数据采集、处理与分析决策三大模块,通过 Java 实现核心功能。利用滑动窗口技术动态分析流量模式,结合阈值检测与机器学习模型识别异常行为。实验表明,该方案在保证高检测准确率的同时支持大规模并发处理,为企业数字化转型提供可靠保障。
140 0
|
7月前
|
存储 机器学习/深度学习 监控
如何监控员工的电脑——基于滑动时间窗口的Java事件聚合算法实现探析​
在企业管理场景中,如何监控员工的电脑操作行为是一个涉及效率与合规性的重要课题。传统方法依赖日志采集或屏幕截图,但数据量庞大且实时性不足。本文提出一种基于滑动时间窗口的事件聚合算法,通过Java语言实现高效、低资源占用的监控逻辑,为如何监控员工的电脑提供一种轻量化解决方案。
193 3
|
8月前
|
存储 监控 算法
基于 PHP 语言的滑动窗口频率统计算法在公司局域网监控电脑日志分析中的应用研究
在当代企业网络架构中,公司局域网监控电脑系统需实时处理海量终端设备产生的连接日志。每台设备平均每分钟生成 3 至 5 条网络请求记录,这对监控系统的数据处理能力提出了极高要求。传统关系型数据库在应对这种高频写入场景时,性能往往难以令人满意。故而,引入特定的内存数据结构与优化算法成为必然选择。
224 3
|
算法
【算法】滑动窗口——最小覆盖子串
【算法】滑动窗口——最小覆盖子串
137 0
|
算法
【算法】滑动窗口——找到字符串中所有字母异位词
【算法】滑动窗口——找到字符串中所有字母异位词
199 0
|
算法
【算法】滑动窗口——将x减到0的最小操作数
【算法】滑动窗口——将x减到0的最小操作数
139 0
|
算法
【算法】滑动窗口——无重复字符的最长子串
【算法】滑动窗口——无重复字符的最长子串
109 0

热门文章

最新文章