数组滑动窗口中的最大值[单调队列]

给定大小为N的数组。数组被分为大小为k的子数组,找到每个子数组的最大值。子数组即为滑动窗口。

如果数组为1 2 3 4 5 6,k=2.

子数组为1 2; 2 3; 3 4; 4 5; 5 6

例如:

Input :
arr[] = {1, 2, 3, 1, 4, 5, 2, 3, 6}
k = 3
Output :
3 3 4 5 5 5 6

Input :
arr[] = {8, 5, 10, 7, 9, 4, 15, 12, 90, 13}
k = 4
Output :
10 10 10 15 15 90 90

方法1  直接计算

使用一个两层循环,即可遍历所有的子数组,并找出其中的最大值。

01 #include<stdio.h>
02  
03 void printKMax(int arr[], int n, int k)
04 {
05     int j, max;
06  
07     for (int i = 0; i <= n-k; i++)
08     {
09         max = arr[i];
10  
11         for (j = 1; j < k; j++)
12         {
13             if (arr[i+j] > max)
14                max = arr[i+j];
15         }
16         printf("%d ", max);
17     }
18 }
19  
20 int main()
21 {
22     int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
23     int n = sizeof(arr)/sizeof(arr[0]);
24     int k = 3;
25     printKMax(arr, n, k);
26     return 0;
27 }

时间复杂度为 O(nk).

方法2 使用平衡二叉树

1) 取出数组中的前K个元素,构建一个平衡二叉树,可以用红黑树或AVL树。

2) 循环 i=0 到 n-k

a) 从二叉树中取出最大的元素,并打印

b) 在二叉树中搜索 arr[i]并删除

c) 将 arr[i+k]插入二叉树

第一步的操作复杂度为O(kLogk),由于循环体每次操作都是lgk,因此总的复杂度为 O(nLogk),总的复杂度为 O(kLogk + (n-k+1)*Logk) ,也就是O(nLogk).

方法3 使用双端队列

创建一个双端队列deque,deque只存储当前的窗口中有用的元素,有用也就是会对未来的选择有影响的元素。插入时保证下面两个性质:

1) 首先,我们保证每次都是队尾插入元素,也就是队首的元素的肯定是距离当前元素最远的。因此每次滑动窗口,只判断队首元素是否超出窗口范围,超出的话则删除。

2) 然后保证deque中的元素是升序的,队尾是最小的元素。由于是从队尾插入,只需要把队尾的元素和当前元素比较即可,不符合条件,则删除队尾元素。

为了判断元素是否超出窗口范围,我们需要存储元素下标。

这种队列,又叫做单调队列,练手题目:POJ 2823

下面是java代码的实现:

01 import java.util.Deque;
02 import java.util.LinkedList;
03  
04 public class MaximumSubArrK {
05  
06     static void printKMax(int arr[],int k){
07         Deque<Integer> deque = new LinkedList<Integer>();
08         for(int i=0; i < arr.length; i++){
09             while(!deque.isEmpty() && (i-deque.peekFirst()>=k) )
10                 deque.pollFirst();
11             while(!deque.isEmpty() && arr[deque.peekLast()] <= arr[i] )
12                 deque.pollLast();
13             deque.addLast(i);
14             if(i >= k-1)
15                 System.out.print(arr[ deque.peekFirst() ] + ",");
16         }
17         System.out.println();
18     }
19  
20     public static void main(String[] args) {
21         int arr[] =  {851079415129013};
22         printKMax(arr ,4);
23  
24     }
25 }

输出:

1 10,10,10,15,15,90,90,

时间复杂度为 O(n)。 因为每个元素都最多会被入队列和出队列一次,总的操作次数不会超过2n。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值