IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 数据结构 - 单调队列 -> 正文阅读

[数据结构与算法]数据结构 - 单调队列

概念

一种具有单调性的队列,分为单调递增队列和单调递减队列。

适合场景

维护区间最值,即,适合解决RMQ(rang maximum/minimum query)问题,也称之为滑动窗口区间最值问题。

若维护区间最小值,则需要维护一个单调递增的序列;若维护区间最大值,则需要维护一个单调递减的序列。

维护单调队列性值的操作

入队操作:队尾入队,会把之前破坏单调性的元素都从队尾移出(维护单调性)。

出队操作:如果队首元素超出区间范围,就将元素从队首出队。

1.1 习题

1) 239. 滑动窗口最大值

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

返回滑动窗口中的最大值。

示例 1:

输入:nums = [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

public int[] maxSlidingWindow(int[] nums, int k) {
    //双端队列,队头、队尾均可操作,用来存放数组元素的下标,然后再去原数组找到下标对应的元素
    //队首元素永远存放的是滑动窗口的最大值
    Deque<Integer> deque = new LinkedList<>();
    int[] result = new int[nums.length - k + 1];//结果数组(存储滑动窗口最大值)大小(窗口滑动次数) = 原数组序列大小 - 窗口大小 + 1
    int idx = 0;
    for (int i = 0; i < nums.length; i++) {
        //出队操作,维护单调队列的递减性
        while (!dequeue.isEmpty() && nums[i] > nums[deque.peekLast()]) {
            deque.pollLast();
        }
        //入队操作
        deque.offerLast(i);
        //当前位置 - 滑动窗口首位元素(原窗口最左侧下标位置) == k, 说明此时滑动窗口队首元素已经不在当前滑动窗口范围,需要将其删除
        if (i - deque.getFirst() == k) {
            deque.pollFirst();
        }
        //当窗口需自前向后滑动进入原数组,i + 1 == k时,滑动窗口首次就位;就位前,不能判定滑动窗口的队首元素是滑动窗口的最大值,不可将其加入结果数组
        if (i + 1 < k) {
            continue;
        }
        result[idx++] = nums[deque.peekFirst()];//滑动窗口的最大值是当前队首元素
    }
    return result;
}

2) 剑指 Offer 59 - II. 队列的最大值

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

示例 1:

输入:
[“MaxQueue”,“push_back”,“push_back”,“max_value”,“pop_front”,“max_value”]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]

class MaxQueue {

    Queue<Integer> queue;
    Deque<Integer> deque;//单调不减的双端队列,用来维护区间(当前主队列中的)最大值

    public MaxQueue() {
        queue = new LinkedList<>();
        deque = new LinkedList<>();
    }

    //取解操作
    public int max_value() {
        return deque.isEmpty() ? -1 : deque.peekFirst();
    }

    public void push_back(int value) {
        queue.offer(value);//元素入主队列
        //为了在主队列元素重复元素出队时匹配到单调队列中相应的元素,所以单调队列中需要存在这些重复元素,即,当前入队元素与单调队列队尾元素相等,则不做出队操作
        while (!deque.isEmpty() && deque.peekLast() < value) {
            deque.pollLast();
        }
        deque.offerLast(value);//元素入单调队列
    }

    public int pop_front() {
        if (queue.isEmpty()) {
            return -1;
        }
        //此时,若主队列中出队的是最当前最大值时,才会满足下面if中的判定条件,同时将单调队列中的队首元素出队
        if (!deque.isEmpty() && queue.peek().equals(deque.peekFirst())) {
            deque.pollFirst();
        }
        return queue.poll();//主队列元素出队,同时返回该出队元素
    }
}

3) 862. 和至少为 K 的最短子数组

返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K

如果没有和至少为 K 的非空子数组,返回 -1

示例 :

输入:A = [2,-1,2], K = 3
输出:3

分析:前缀和数组 + 单调递增队列

子数组和 => 前缀和数组,

前缀和数组两元素差值的最小值为k,即,若固定前缀和数组末尾后,该固定末尾的子数组两元素差值需要尽可能大,即直接用当前前缀和数组固定末尾元素减去前缀和数组的最小值,得到的就是那个最大的差值,若这个差值>=k,则,为了找到长度更短的子数组,可以继续向后找到前缀和数组的次小值,看是否满足差值>=k;直到差值不满足>=k。最后一个满足条件的就是固定末尾的最短子数组。==》 由此分析,可得,除了需要一个前缀和数组外,还需要维护这个前缀和数组的最小值的单调递增队列;

对该前缀和数组从前后扫描,以前缀和数组的下一个元素为固定末尾,寻找满足题意的子数组,因为需要找比之前的子数组还短的数组,所以之前从单调队列队首弹出的元素都不满足条件,可以基于此单调队列从前至后继续寻找。

public int shortestSubarray(int[] nums, int k) {
    Deque<Integer> deque = new LinkedList<>();
    int[] sum = new int[nums.length + 1];
    for (int i = 0; i < nums.length; i++) {
        sum[i + 1] = sum[i] + nums[i];
    }
    int pos = -1;
    int ans = -1;
    for (int i = 0; i < sum.length; i++) {
        //维护一个单调递增的单调队列
        while (!deque.isEmpty() && sum[i] < sum[deque.peekLast()]) {
            deque.pollLast();
        }
        deque.offerLast(i);
        //以当前元素为固定结尾,在单调递增队列中从前向后查找满足题意的元素。
        //队首元素是最小的,若当前前缀和数组元素 - 队首元素都不满足题意,则减去后面更大的元素更不会满足题意
        while (!deque.isEmpty() && sum[i] - sum[deque.peekFirst()] >= k) {
            pos = deque.pollFirst();
        }
        //对于每个前缀和数组中的元素,查找结束后,若找到一个更短的子数组,则更新结果。
        if (pos != -1 && (i - pos < ans || ans == -1)) {
            ans = i - pos;
        }
    }
    return ans;
}

4) 1438. 绝对差不超过限制的最长连续子数组

给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。

如果不存在满足条件的子数组,则返回 0 。

示例 1:

输入:nums = [8,2,4,7], limit = 4
输出:2
解释:所有子数组如下:
[8] 最大绝对差 |8-8| = 0 <= 4.
[8,2] 最大绝对差 |8-2| = 6 > 4.
[8,2,4] 最大绝对差 |8-2| = 6 > 4.
[8,2,4,7] 最大绝对差 |8-2| = 6 > 4.
[2] 最大绝对差 |2-2| = 0 <= 4.
[2,4] 最大绝对差 |2-4| = 2 <= 4.
[2,4,7] 最大绝对差 |2-7| = 5 > 4.
[4] 最大绝对差 |4-4| = 0 <= 4.
[4,7] 最大绝对差 |4-7| = 3 <= 4.
[7] 最大绝对差 |7-7| = 0 <= 4.
因此,满足题意的最长子数组的长度为 2 。

L为最长的子数组,若最大值 - 最小值 <= limit,则该子数组内任意两个元素之间的绝对值差一定<=limit;故,一定存在满足条件的小于L的子数组;一定不存在满足条件的大于L的子数组;

由此,此求最长子数组长度可转化为二分查找的01模型中的找最后一个0的问题。

class Solution {
    public int longestSubarray(int[] nums, int limit) {
        return binarySearch(nums, 1, nums.length, limit);
    }

    /* L为最长的子数组,若最大值 - 最小值 <= limit,则该子数组内任意两个元素之间的绝对值差一定<=limit;故,一定存在满足条件的小于L的子数组;一定不存      * 在满足条件的大于L的子数组;
     * 由此,此求最长子数组长度可转化为二分查找的01模型中的找最后一个0的问题。
     */
    private int binarySearch(int[] nums, int l, int r, int limit) {
        int mid = 0;
        while (l < r) {
            mid = (l + r + 1) >> 1;//滑动窗口大小
            if (check(nums, mid, limit)) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }
        return l;
    }

    private boolean check(int[] nums, int length, int limit) {
        Deque<Integer> deque_min = new LinkedList<>();
        Deque<Integer> deque_max = new LinkedList<>();
        for (int i = 0; i < nums.length; i++) {
            while (!deque_max.isEmpty() && nums[i] > nums[deque_max.peekLast()]) {
                deque_max.pollLast();
            }
            while (!deque_min.isEmpty() && nums[i] < nums[deque_min.peekLast()]) {
                deque_min.pollLast();
            }
            deque_max.offerLast(i);
            deque_min.offerLast(i);
            if (i + 1 < length) continue;
            if (i - deque_max.peekFirst() == length) {
                deque_max.pollFirst();
            }
            if (i - deque_min.peekFirst() == length) {
                deque_min.pollFirst();
            }
            if (nums[deque_max.peekFirst()] - nums[deque_min.peekFirst()] <= limit) {
                return true;
            }
        }
        return false;
    }
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-26 12:18:24  更:2021-07-26 12:20:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/23 23:24:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码