什么是滑动窗口算法?例子?

Posted

技术标签:

【中文标题】什么是滑动窗口算法?例子?【英文标题】:What is Sliding Window Algorithm? Examples? 【发布时间】:2012-01-06 09:09:39 【问题描述】:

在解决几何问题时,我遇到了一种称为滑动窗口算法的方法。

真的找不到任何学习资料/细节。

算法是关于什么的?

【问题讨论】:

你说的是this kind of windows 吗?我没有看到与几何的链接。 除非您描述几何问题,否则无法解释连接。 不,我们说的是这种类型的窗口:plygem.com/wp-content/uploads/2018/09/T3_Horizontal_Slider.gif 这里是一个例子:leetcode.com/problems/… 【参考方案1】:

一般来说,滑动窗口是在底层集合上运行的子列表。即,如果你有一个像

这样的数组
[a b c d e f g h]

一个大小为 3 的滑动窗口会像这样在上面运行

[a b c]
  [b c d]
    [c d e]
      [d e f]
        [e f g]
          [f g h]

如果您想计算一个移动平均值,或者如果您想创建一组所有相邻对等,这很有用。

【讨论】:

嗨,我有一个关于滑动窗口的问题。例如,我有 1 年的信息,我使用 4 个月的窗口来分析它。有随时间变化的变量。如果我正在分析年中的 4 个月,我是使用窗口大小来计算这 4 个月的指标,还是使用窗口末尾对应 6 或 7 个月信息的变量值计算?在这种情况下,我指的是一次增长一个月的变量。 您的问题有点不清楚,但假设您有以下情况:[5, 10, 7, 13, 19, 14, 3, 13, 17, 10, 22, 2] 代表 1 月、2 月、...、12 月。如果你的窗口大小是 4,年中会有这个窗口:[19, 14, 3, 13]。就这样。例如,如果您正在计算运行平均值,则年中的平均值将为(19+14+3+13)/4。这回答了你的问题吗? 它确实给了我更多的洞察力。我想通过给出这个例子来扩展:有 6 个月和 3 个客户。 1号客户从一开始就存在,2号客户出现在第三个月,3号客户出现在第五个月。例如,这个想法是使用滑动窗口来计算每月平均值。我的问题是,由于滑动窗口移动了一个月,如果窗口捕捉到它们都存在的月份,客户的平均值会有所不同?如果不清楚,我可以详细说明。【参考方案2】:

滑动窗口是一种解决涉及数组/列表问题的技术。在 O(n^2) 或 O(n^3) 中使用蛮力方法很容易解决这些问题。 使用“滑动窗口”技术,我们可以将时间复杂度降低到 O(n)。

关于这方面的精彩文章在这里:https://medium.com/outco/how-to-solve-sliding-window-problems-28d67601a66

因此,您要做的第一件事就是发现问题 使用滑动窗口范例。幸运的是,有一些常见的 赠品:

问题将涉及到一个像数组或字符串一样有序且可迭代的数据结构

您正在该数组/字符串中查找某个子范围,例如最长、最短或目标值。

有一个明显的幼稚或蛮力解决方案,其运行时间复杂度为 O(N²)、O(2^N) 或其他一些较大的时间复杂度。

但最大的收获是您正在寻找的东西是 通常是某种最优的,比如最长的序列或最短的序列 完全满足给定条件的事物的序列。

【讨论】:

这应该被标记为正确答案 - 其他答案是关于“卷积”而不是关于这个滑动窗口算法。【参考方案3】:

为了补充前面的答案,这里有更多资源可以很好地说明这个概念。

This youtube video 是我在这个主题上找到的最好的。

Here 是 leetcode 上可以使用这种技术解决的问题列表

滑动窗口是***公司的编码轮次中最常见的话题之一,因此绝对值得花一些时间来掌握它。

【讨论】:

【参考方案4】:

我认为它更像是一种技术,而不是一种算法。这是一种可用于各种算法的技术。

我认为通过以下示例可以最好地理解该技术。想象一下我们有这个数组:

[ 5, 7, 1, 4, 3, 6, 2, 9, 2 ]

我们如何找到五个连续元素的最大和?好吧,我们首先查看5, 7, 1, 4, 3 并看到总和是20。然后我们将查看下一组五个连续元素,即7, 1, 4, 3, 6。这些总和是21。这比我们之前的总和还要多,所以7, 1, 4, 3, 6 是目前我们迄今为止得到的最好的。

让我们看看我们是否可以改进。 1, 4, 3, 6, 2?不,总和为164, 3, 6, 2, 9?总和为24,所以现在这是我们得到的最佳序列。现在我们进入下一个序列,3, 6, 2, 9, 2。这个总和为22,并没有超过我们目前最好的24。我们已经到了尽头,所以我们完成了。

以编程方式实现此功能的蛮力方法如下:

const getMaxSumOfFiveContiguousElements = (arr) => 
  let maxSum = -Infinity;
  let currSum;

  for (let i = 0; i <= arr.length - 5; i++) 
    currSum = 0;

    for (let j = i; j < i + 5; j++) 
      currSum += arr[j];
    

    maxSum = Math.max(maxSum, currSum);
  

  return maxSum;
;

这个的时间复杂度是多少?这是O(n*k)。外层循环遍历n - k + 1 项目,但是当nk 大得多时,我们可以忘记k + 1 部分,而将其称为n 项目。然后内部循环通过k 项目,所以我们有O(n*k)。尝试像这样可视化它:

我们可以将其简化为 O(n) 吗?让我们回到这个数组:

[ 5, 7, 1, 4, 3, 6, 2, 9, 2 ]

首先我们得到5, 7, 1, 4, 3 的总和。接下来我们需要7, 1, 4, 3, 6 的总和。像这样可视化它,每组五个元素都有一个“窗口”。

第一个窗口和第二个窗口有什么区别?嗯,第二个窗口去掉了左边的5,但在右边添加了一个6。因此,既然我们知道第一个窗口的总和是20,为了得到第二个窗口的总和,我们取20,减去5,并加上6,得到21。我们实际上不必遍历第二个窗口中的每个元素并将它们相加 (7 + 1 + 4 + 3 + 6)。这将涉及重复和不必要的工作。

这里的滑动窗口方法最终是两个操作而不是五个,因为k5。这并不是一个巨大的改进,但您可以想象,对于更大的k(和更大的n)它确实有帮助。

以下是使用滑动窗口技术的代码的工作方式:

const getLargestSumOfFiveConsecutiveElements = (arr) => 
  let currSum = getSum(arr, 0, 4);
  let largestSum = currSum;

  for (let i = 1; i <= arr.length - 5; i++) 
    currSum -= arr[i - 1]; // subtract element to the left of curr window
    currSum += arr[i + 4]; // add last element in curr window
    largestSum = Math.max(largestSum, currSum);
  

  return largestSum;
;

const getSum = (arr, start, end) => 
  let sum = 0;

  for (let i = start; i <= end; i++) 
    sum += arr[i];
  

  return sum;
;

这就是滑动窗口技术的要点。在其他问题中,您可能会做一些比获取窗口内元素的总和更复杂的事情。或者窗口本身的大小可能不同,而不是我们在这里看到的固定大小的五个。但是滑动窗口技术的这个基本应用应该为您提供一个可以构建的基础。

【讨论】:

令人惊叹的插图。感谢您抽出宝贵时间来做这件事。 @Ezio 非常感谢您的称赞,谢谢。弄清楚这样的插图真的是我必须为自己做的事情才能理解它:) 当然。我们是计算机程序员,但我们必须掌握使用笔和纸解决问题的艺术。 在第二个插图中,金点不应该在第二个。滑动? (第一个索引而不是 0)还是我遗漏了什么? @PartOfTheOhana 我使用了Sketch,这是一种轻量级的 Photoshop。作为替代方案,我最近遇到了Excalidraw,它是免费的,而且对于像这样的可视化来说似乎也是一个不错的选择。

以上是关于什么是滑动窗口算法?例子?的主要内容,如果未能解决你的问题,请参考以下文章

滑动窗口算法

基础算法滑动窗口

基础算法滑动窗口

算法总结之滑动窗口

leetcode之滑动窗口算法小结

leetcode之滑动窗口算法小结