滑动窗口算法(一)
某日点开sentinel-core代码学习,想看看qps、rt等是怎么统计的。
点开StatisticSlot类,发现里面是用DefaultNode增加qps,然后尝试点开
DefaultNode->StatisticNode->ArrayMetric->MetricsLeapArray->LeapArray...
晕...怎么这么多类- -||| 中间还有MetricBucket、LongAdder...
其中LeapArray类有个属性AtomicReferenceArray<WindowWrap<T>> array,表示这个jdk类也是第一次见orz
看注释写道Using sliding window algorithm,使用了滑动窗口算法。可是这代码太深奥T_T,表示打扰了,还是洗洗睡了Zzz....
--------------------------------------------------------------------------------------------------------------------------------------------
言归正传,虽然sentinel中的代码还看不懂,通过滑动窗口几个字,百度了解一下:)
找到一个经典的问题:
(一)给定一组大小为n的整数数组,计算长度为k的子数组和的最大值。
比如
数组为:1,2,3,4
最大值为:3+4=7
数组为:-1,4,7,-3,8,5,-2,6
最大值为:7-3+8=12
想到最简单思路,那就遍历所有子数组呗,求和然后比较。
int index = 0;// 记录最大子数组第1个元素的索引,目前是0 int maxSum = 0;// 记录最大子数组和,目前是从左开始第1个子数组 for (int i = 0; i < k; i++) { maxSum += array[i]; } for (int i = 1; i <= array.length - k; i++) {// 遍历所有子数组,求和并比较 int curSum = 0; for (int j=0; j < k; j++) {// 计算当前子数组和 curSum += array[i + j]; } if (curSum > maxSum) {// 如果大于最大和,则记录 maxSum = curSum; index = i; } }
运用滑动窗口思路,遍历时不嵌套循环计算所有值;外层遍历相当于窗口向右滑动,每次减去失效值加上最新值,即为当前窗口的和,然后再比较。
int index = 0;// 记录最大子数组第1个元素的索引,目前是0 int maxSum = 0;// 记录最大子数组和,目前是从左开始第1个子数组 for (int i = 0; i < k; i++) { maxSum += array[i]; } int curWindowSum = maxSum; for (int i = 1; i <= array.length - k; i++) {// 从下个元素开始,即窗口向右滑动 curWindowSum = curWindowSum - array[i - 1] + array[k + i - 1];// 减去失效值,加上最新值 if (curWindowSum > maxSum) {// 如果大于最大和,则记录 maxSum = curWindowSum; index = i; } }
可以看到代码差不多,只不过在计算求和时,采取了滑动窗口技术(思路),通过一减一加求和,消除了内部的循环。
注:这里为了突出语义,将变量名curSum改为curWindowSum
完整代码如下:
package com.cdfive.learn2018.algorithm; /** * 求数组array长度为k的子数组的最大和 * * 算法1-cal * 遍历所有子数组,求和并比较 * 嵌套循环 O(n*k) * * 算法2-calByLeapWinow * 窗口向右滑动,减去失效值加上最新值 * 单层循环 O(n) * * input: array=[1,2,3,4] k=2 * output: 7 // 3+4 * * input: array=[-1,4,7,-3,8,5,-2,6] k=3 * output: 12 // 7-3+8 * * @author cdfive * @date 2018-12-02 */ public class SimpleLeapWindow1 { public static void main(String[] args) { cal(new int[]{1, 2, 3, 4}, 2); cal(new int[]{-1,4,7,-3,8,5,-2,6}, 3); System.out.println("-------------"); calByLeapWinow(new int[]{1, 2, 3, 4}, 2); calByLeapWinow(new int[]{-1,4,7,-3,8,5,-2,6}, 3); } /** * 遍历所有子数组,求和并比较 * 嵌套循环 O(n*k) */ public static void cal(int[] array, int k) { if (array.length == 0 || k <= 0 || k > array.length) {// 非法参数不处理 return; } int index = 0;// 记录最大子数组第1个元素的索引,目前是0 int maxSum = 0;// 记录最大子数组和,目前是从左开始第1个子数组 for (int i = 0; i < k; i++) { maxSum += array[i]; } for (int i = 1; i <= array.length - k; i++) {// 遍历所有子数组,求和并比较 int curSum = 0; for (int j=0; j < k; j++) {// 计算当前子数组和 curSum += array[i + j]; } if (curSum > maxSum) {// 如果大于最大和,则记录 maxSum = curSum; index = i; } } /**打印结果*/ System.out.print(maxSum + " // ");// 打印最大和 System.out.print(array[index]);// 先打印第1个值 for (int i = 1; i < k; i++) { int value = array[i + index]; System.out.print(value >= 0 ? ("+" + value) : value);// 非负数前面打印+号 } System.out.println(); } /** * 窗口向右滑动,通过减失效值加最新值求和并比较 * 单层循环 O(n) */ public static void calByLeapWinow(int[] array, int k) { if (array.length == 0 || k <= 0 || k > array.length) {// 非法参数不处理 return; } int index = 0;// 记录最大子数组第1个元素的索引,目前是0 int maxSum = 0;// 记录最大子数组和,目前是从左开始第1个子数组 for (int i = 0; i < k; i++) { maxSum += array[i]; } int curWindowSum = maxSum; for (int i = 1; i <= array.length - k; i++) {// 从下个元素开始,即窗口向右滑动 curWindowSum = curWindowSum - array[i - 1] + array[k + i - 1];// 减去失效值,加上最新值 if (curWindowSum > maxSum) {// 如果大于最大和,则记录 maxSum = curWindowSum; index = i; } } /**打印结果*/ System.out.print(maxSum + " // ");// 打印最大和 System.out.print(array[index]);// 先打印第1个值 for (int i = 1; i < k; i++) { int value = array[i + index]; System.out.print(value >= 0 ? ("+" + value) : value);// 非负数前面打印+号 } System.out.println(); } }
--------------------------------------------------------------------------------------------------------------------------------------------
参考:
滑动窗口法详解 https://blog.csdn.net/sty945/article/details/79846516
Window Sliding Technique https://www.geeksforgeeks.org/window-sliding-technique/
[LeetCode]Sliding Window Algorithm相关题目总结 https://blog.csdn.net/Ellie_/article/details/76781877