算法-数组 求数组中滑动窗口的最大数值列表
这个题目是在一个数组中,有一个滑动窗口,滑动窗口从数组的左边(i=0)开始,依次往右边滑动,获取每滑动一次的窗口内的最大值,得到一个列表进行返回
比如数组 int[] a = { 4, 3, 5, 4, 3, 3, 6, 7 }; 滑动窗口 w = 3,那么返回值列表如下:
1. 滑动窗口在数组中的起始位置 i = 0, 滑动窗口 window是3 (w = 3), 所以窗口内元素是 {4,3,5} => 获取窗口内最大元素 -> 5
2. 滑动窗口往右边滑动一个位置i=1, 滑动窗口内元素是 {3,5,4} => 获取窗口内最大元素 -> 5
3. 滑动窗口继续一个位置一个位置往右边滑动
i =2 => 滑动窗口 {5,4,3} => 获取窗口内最大元素 -> 5
i =3 => 滑动窗口 {4,3,3} => 获取窗口内最大元素 -> 4
i =4 => 滑动窗口 {3,3,6} => 获取窗口内最大元素 -> 6
i =5 => 滑动窗口 {3,6,7} => 获取窗口内最大元素 -> 7
i =6 => 滑动窗口 {6,7} 已经不到w=3的3个元素,所以结束
最终结果就是 [5,5,5,4,6,7]
从上面的过程中,我们可以得出
如果滑动窗口左边的位置是数组位置i,那么滑动窗口右边的位置就是 (i+w-1), 也就是说,滑动窗口的范围是 [i, i + w - 1]
如果滑动窗口右边的位置是数组位置i, 那么滑动窗口左边的位置就是 (i - w + 1), 也就是说,滑动窗口的范围是 [i-w+1, i]
最后得到的结果集数组的长度是 原始数组长度 n - 滑动窗口大小 w + 1 = n - w + 1
这道题目,我们需要使用双向队列来实现. 双向队列是这样的

由于C#中并没有双向队列,所以我们使用C#中的 双向链表 LinkedList来模拟这个双向队列





滑动窗口这类型题目,我们一般都是使用双向列表来进行解答,双向队列可以快速地进行插入和删除操作,从而快速地计算出窗口内的最大值或者最小值。
public static int[] SlidingWindowMax(int[] nums, int windowSize) { // 1.边界判断:数组为空、窗口非法,直接返回空数组 if (nums == null || nums.Length == 0 || windowSize <= 0) return Array.Empty<int>(); // 保存每个窗口的最大值结果 List<int> result = new List<int>(); // 双端队列:存放数组【下标】,不是数值!队列内对应数值单调递减 LinkedList<int> deque = new LinkedList<int>(); // 遍历数组 i 是当前元素下标 for (int i = 0; i < nums.Length; i++) { // ==========① 清理队尾:维持单调递减队列========== // 如果队列不为空,并且当前数字 >= 队尾下标对应的数值 // 队尾这个数,只要当前i还在窗口内,它永远不可能成为窗口最大值,直接删掉 while (deque.Count > 0 && nums[i] >= nums[deque.Last.Value]) { deque.RemoveLast(); } // 当前下标加入队列尾部 deque.AddLast(i); // ==========② 清理队首:把已经滑出窗口的下标移除========== // 当前窗口左边界 left = i - windowSize + 1 // 队首下标 < left,代表这个元素已经不在当前窗口里,过期了 while (deque.Count > 0 && deque.First.Value < i - windowSize + 1) { deque.RemoveFirst(); } // ==========③ 窗口已满,开始记录最大值========== // i >= windowSize -1:窗口长度凑够windowSize,第一个窗口形成 if (i >= windowSize - 1) { // 队首下标对应的数字就是当前窗口最大值 result.Add(nums[deque.First.Value]); } } // List转数组返回 return result.ToArray(); }
浙公网安备 33010602011771号