ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

生成窗口最大数组(优先队列、单调队列)

2022-02-19 22:35:23  阅读:105  来源: 互联网

标签:arr 窗口 队列 最大值 元素 int 数组 单调


生成窗口最大数组(优先队列、单调队列)

问题重述:

给你一个整数数组 arr,有一个大小为 w 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值

示例 1:

输入:arr = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

问题分析:

1.暴力求解:这道题可以使用暴力求解,直接在每一次窗口向右滑动时,遍历一遍数组,即可得出最大值数组,但这样时间复杂度为O(nk)

2.优先队列:我们可以使用记录每一个窗口的最大值,每一次向右移动窗口时,将当前值和上一个最大值进行对比,但是这样会产生一个问题,当上一个窗口的最大值处于窗口最左端时,此时我们向右滑动窗口会造成错误,最大值可能并不存在当前的窗口中,因此我们每一次向右滑动都对最大值队列进行判断,判断当前存放的上一个队列的最大值是否处于滑动窗口中,不处于则删除,处于则添加新增的值并进行判断(我们可以直接使用优先队列,设置好排序规则,添加时就不需要我们自己进行判断力),这种方法时间复杂组为O(nlogn)

3.单调队列:创建一个队列,每次添加时,进行条件判断,如果队列的尾部元素对应数值小于当前将要添加的数值,将当前队尾元素删除,直到大于等于为止,将当前数值对应元素加入队列。和优先队列一样,我们要判断最大元素对应值是否在窗口中,所以要进行判断。时间复杂度为O(N)

解题方法:

暴力求解、优先队列、单调队列

解题:

代码:
package cn.basic.algorithm;

import java.util.LinkedList;
import java.util.PriorityQueue;

/**
 * @author FOLDN
 *  生成窗口最大值数组
 */
public class MaxWindow {
    public static void main(String[] args) {
        int[] arr = {1,3,-1,-3,5,3,6,7};
        int[] maxWindow = getMaxWindow1(arr, 3);
        for (int i = 0; i < maxWindow.length; i++) {
            System.out.print(maxWindow[i]);
        }
    }
    // 优先队列
    public static int[] getMaxWindow1(int[] arr,int w){
        if (arr == null || w <1 || arr.length < w){
            return null;
        }
        int[] maxArr = new int[arr.length - w +1];
        //创建优先队列,定义比较规则(使用lambda表达式)
        PriorityQueue<int[]> pq = new PriorityQueue<>((q1,q2)->q1[0] != q2[0] ? q2[0] - q1[0]: q2[1] - q1[1]);
        //先将前w个元素进行比较
        for (int i = 0; i < w; i++) {
            pq.offer(new int[]{arr[i],i});
        }
        // 将前w个元素中最大值和下标加入优先队列
        maxArr[0] = pq.peek()[0];
        for (int i = w; i < arr.length; i++) {
            // 将当前元素加入队列
            pq.offer(new int[]{arr[i],i});
            // 将不属于当前窗口的元素和下标删除
            while (pq.peek()[1] <= i-w){
                pq.poll();
            }
            // 将最大值加入最大值数组中
            maxArr[i-w+1] = pq.peek()[0];
        }
        return maxArr;
    }
    // 单调队列(最优解)
    public static int[] getMaxWindow2(int[] arr, int w){
        if (arr == null || w <1 || arr.length < w){
            return null;
        }
        int index = 0;
        int[] maxArr = new int[arr.length - w +1];
        LinkedList<Integer> qMax = new LinkedList<>();
        for (int i = 0; i < arr.length; i++) {
            // 将不满足条件的y
            while (!qMax.isEmpty() && arr[qMax.peekLast()] < arr[i]){
                qMax.pollLast();
            }
            qMax.addLast(i);
            // 判断元素是否属于窗口中,这里的条件判断用while循环也可以
            if (qMax.peekFirst() == i - w){
                qMax.pollFirst();
            }
            
            if (i >= w -1){
                maxArr[index++] = arr[qMax.peekFirst()];
            }
        }
        return maxArr;
    }
}

代码解析:

优先队列:

首先输入的值要满足题目条件,不满足条件则抛出异常或者返回空值。我们需要定义一个数组来存放每个窗口的最大值,该数组大小为**arr长度-w+1**。创建一个优先队列,在构造方法中重新定义**Comparator**来进行排序规则的设置。先比较前w个元素,然后从前w个元素在进行比较,在加入新元素后,每一次都要判断当前队列的头部元素是否属于当前窗口,如果不满足则删除,直到满足为止。然后就可以将当前队列头部元素对应的数值加入最大数组了(因为优先队列已经有顺序了)

单调队列:

	首先输入的值要满足题目条件,不满足条件则抛出异常或者返回空值。我们需要定义一个数组来存放每个窗口的最大值,该数组大小为**arr长度-w+1**。创建一个单调队列(队列继承了**LindedList**,所以我们直接使用**LinkedList**也一样)。对数组进行循环,每一个元素添加前都对单调队列进行更新,始终保持当前要添加的元素小于当前元素,然后添加当前元素到队列尾部。当不满足条件时,删除队列尾部元素直到满足条件。添加完成后要记得保证当前的最大元素属于当前窗口中。
总结:

优先队列:Java中一种数据结构,可以通过我们自己定义排序规则(添加时会自动排序,不需要我们进行排序),当我们需要求某个元素的最大值最小值时就可以用到这种结构。

单调队列:队列内的元素按照规则排序,如果压入的元素不符合规则,则弹出队尾元素,直到满足规则。(头尾都可以删除,但是只有尾部才能添加)

拓展:单调栈

单调栈:栈内元素按照规则排序,如果压入的元素不符合规则,则弹出队尾元素,直到满足规则。(只能操作栈顶)

单调栈一般用于求距离自己较近的最大值或者最小值,单调队列一般用于维护某一段区间内的最大值或者最小值

标签:arr,窗口,队列,最大值,元素,int,数组,单调
来源: https://www.cnblogs.com/foldn/p/15913865.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有