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


80

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

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

算法是关于什么的?


3
您是在谈论这种窗户吗?我看不到与几何的链接。
Alexandre C.

除非您描述几何问题,否则无法解释连接。
唐·罗比

Answers:


157

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

[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]

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


34

滑动窗口是解决涉及阵列/列表的问题的技术。使用O(n ^ 2)或O(n ^ 3)中的蛮力方法可以轻松解决这些问题。 使用“滑动窗口”技术,我们可以将时间复杂度降低为O(n)。

伟大的文章在这里:https//medium.com/outco/how-to-solve-sliding-window-problems-28d67601a66

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

  • 问题将涉及一个有序且可迭代的数据结构,如数组或字符串

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

  • 在O(N²),O(2 ^ N)或其他较大的时间复杂度下,有一个简单的幼稚或蛮力解决方案。

但是最大的礼物是,您正在寻找的东西通常是某种最优的,例如恰好满足给定条件的事物的最长序列或最短序列。


22

我认为它更多的是技术而不是算法。这是一种可以在各种算法中使用的技术。

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

[ 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项目,但是当n大于时k,我们可以忽略该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;
};

这就是滑动窗口技术的要点。在其他问题中,您可能要做的事情比获取窗口内元素的总和还要复杂。否则,窗口本身的大小可能会有所变化,而不是我们在此处看到的固定大小(五个)。但是,滑动窗口技术的这一基本应用应该为您提供一个基础,您可以以此为基础。


6

除了前面的答案以外,这里还有更多资源可以很好地说明这一概念。

是我在该主题上发现的最好的youtube视频

是有关leetcode的问题列表,可以使用此技术解决

在顶级公司的编码回合中,滑动窗口是最常见的主题之一,因此绝对值得花一些时间来掌握它

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.