《程序员代码面试指南》生成窗口最大值数组

题目:

有一个整型数组 arr 和一个大小为 w 的窗口从数组的最左边滑倒最右边,窗口每次向右边划一个位置。

例如:

数组为 [4,3,5,4,3,3,6,7],窗口大小为3时:

 

[4 3 5] 4 3 3 6 7 窗口的最大值为5

 4[3 5 4] 3 3 6 7 窗口的最大值为5

 4 3[5 4 3] 3 6 7 窗口的最大值为5

 4 3 5[4 3 3] 6 7 窗口的最大值为4

 4 3 5 4[3 3  6]7 窗口的最大值为6

 4 3 5 4 3[3  6 7] 窗口的最大值为7

如果数组的长度为 n ,窗口大小为 w ,则一共可以产生 n - w + 1个窗口最大值。

请实现一个函数。

输入:整型数组 arr, 窗口大小为 w。

输出:一个长度为 n - w + 1的数组 res ,res[i]表示每一种窗口状态下的最大值,以本题为例,结果应该返回 [5,5,5,4,6,7]。

解答:

如果数组的长度为 N, 窗口的大小为 w,求一个时间复杂度 O(N)的解法。

本题的关键在于用双端队列来实现窗口最大值的更新,首先生成双端队列 qmax,qmax中放入数组 arr中的下标。

假设遍历到 arr[i],qmax的放入规则:

  1. 如果qmax 为空,直接把下标 i 放入 qmax,放入过程结束。

  2. 如果qmax 不为空,取出当前 qmax 队尾存放的下标,假设为 j。

       1)如果 arr[j] > arr[i], 直接把下标 i 放进 qmax 的队尾,放入过程结束。

       2)如果 arr[j] <= arr[i],把 j 从qmax 中弹出,继续 qmax 的放入规则

假设遍历到 arr[i], qmax的弹出规则为:

如果 qmax 队头的下标等于 i - w 表示当前 qmax 队头下标已经过期,弹出当前队头的下标即可。

 

 

根据如上的放入和弹出规则,qmax便成了一个维护窗口为w的最大值更新结构。下面以本题为例:

1.开始时 qmax  为空,qmax={}

 

2.遍历到arr[0] == 4,将下标 0 放入 qmax, qmax = {0}。

 

3.遍历到arr[1] == 3,当前qmax队尾下标为0,又有arr[0] > arr[1],所以将下标 1 放入 qmax 的尾部,qmax={0,1}。

 

4.遍历到arr[2] == 5,当前qmax的队尾的下标为 1,又有arr[1] <= arr[2]所以将 1 从队列中弹出,qmax 变为{0}。当前的 qmax 的队尾下标为 0 ,由于 arr[0] < arr[2],将当前下标 0 从qmax 尾部弹出,qmax 当前变为 {}。将 2 放入 qmax ,qmax = {2}。此时窗口 arr[0..2]出现了,当前 qmax 队头的下标为2,所有当前 arr[0..2]最大值为5。

 

5.遍历到 arr[3] == 4,当前 qmax的队尾下标为2,又有 arr[2] > arr[3],所以将 3 放入 qmax 尾部,qmax =  {2,3}窗口{1..3}出现,当前 qmax 队头的下标为2,这个下标还没有过期,所以窗口arr[1..a]的最大值为arr[2]( 即5)

 

6.遍历到 arr[4] == 3,当前qmax下标为3,又有arr[3] > arr[4], 所以将下标4放入qmax尾部,qmax={2,3,4}窗口[2..4]出现,当前qmax队列的队头下标为2,这个下标还没有过期。

 

7.遍历到arr[5] == 3,当前qmax队尾下标为4,又有arr[4] <= arr[5],所以将下标4从qmax尾部弹出,qmax变为{2,3}。当前qmax的队尾变为3,又有arr[3]>arr[5],所以将5放入qmax尾部,qmax = {2,3,5}当前qmax队头下标为2,所以将2从头部弹出,qmax变为{3,5}当前qmax队头的下标为3这个下标没有过期所以窗口arr[3..5]的最大值为arr[3]即4。

 

8.遍历到arr[6] == 6,当前qmax队尾的下标变为5,又有arr[5]<=arr[6]所以将下标5从队列尾部弹出,qmax变为{3}。当前qmax的队尾下标为3,又有arr[3] <= arr[6],所以将下标3从qmax尾部弹出,qmax变为{}。将下标6放入qmax。

窗口[4..6]出现了(即6)。

 

9.遍历到arr[7] == 7,当前qmax的队尾下标为6,又有arr[6]<=arr[7],所以将下标6从qmax尾部弹出将7放入qmax,qmax={7}。窗口arr[5..7]出现,当前qmax队头下标为7,这个下标没有过期,所以窗口arr[5..7]最大值为arr[7](即7)。

 

10.依次将出现的窗口最大值[5,5,5,4,6,7],在遍历的时候收集起来,最后返回即可。

上述过程中,每个下标值最多进qmax一次,出qmax一次。所以遍历的过程中进出双端队列的操作时间复杂度O(N),整体的时间复杂度O(N)

import java.util.LinkedList;



public class GetMaxWindows {

    public int[] getMaxWindows(int[] arr, int w){

        if (arr == null || w < 1 || arr.length < w){

            return null;

        }

        LinkedList<Integer> qmax = new LinkedList<Integer>();

        int[] res = new int[arr.length - w + 1];

        int index = 0;

        for(int i = 0; i < arr.length; i++){

            while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i]){

                qmax.pollLast();

            }

            qmax.addLast(i);

            //判断当前队列头部的坐标是否已经过期

            if (qmax.peekFirst() == i - w){

                qmax.pollFirst();

            }

            //将窗口最大值的下标存入 res 数组中

            if(i >= w - 1){

                res[index++] = arr[qmax.peekFirst()];

            }

        }

        return res;

    }

    public static void main(String[] args) {

        

    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值