zoukankan      html  css  js  c++  java
  • ADT:单调队列(Leetcode1438)

    ADT:单调队列(Leetcode1438)

    题目描述

    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 。
    示例 2:
    
    输入:nums = [10,1,2,4,7,2], limit = 5
    输出:4 
    解释:满足题意的最长子数组是 [2,4,7,2],其最大绝对差 |2-7| = 5 <= 5 。
    示例 3:
    
    输入:nums = [4,2,2,2,4,4,2,2], limit = 0
    输出:3
    

    解题

    滑动窗口

    很直观的就是使用双指针的滑动窗口实现,主要是需要维护一个数据结构来记录limit

    先把抽象出ADT的滑动窗口实现写出来

    class Solution {
    public:
        int longestSubarray(vector<int> &nums, int limit) {
            MQ mq;
            int maxLen = 0;
            int left=0,right=0;
            while (right<nums.size()){
                mq.add(nums[right++]);
                while (mq.limit()>limit){
                    mq.remove(nums[left++]);
                }
                maxLen = max(maxLen,right-left);
            }
            return maxLen;
        }
    };
    

    重点在于实现一个数据结构,又add(int n)remove(int n)limit()方法

    使用堆实现

    参考ADT:双优先队列,解决中位数问题#,使用堆+延迟删除实现

    class Heap{
        // 大顶堆
        priority_queue<int, vector<int>, less<int>> big_heap;
        // 小顶堆
        priority_queue<int, vector<int>, greater<int>> small_heap;
        unordered_map<int, int> big_delay_delete;
        unordered_map<int, int> small_delay_delete;
    public:
        void add(int n){
            big_heap.push(n);
            small_heap.push(n);
        }
        int limit(){
            return big_heap.top()-small_heap.top();
        }
        void remove(int n){
            if(big_heap.top()==n){
                big_heap.pop();
            }else{
                big_delay_delete[n]++;
            }
            while (big_delay_delete[big_heap.top()]>0){
                big_delay_delete[big_heap.top()]--;
                big_heap.pop();
            }
            if(small_heap.top()==n){
                small_heap.pop();
            }else{
                small_delay_delete[n]++;
            }
            while (small_delay_delete[small_heap.top()]>0){
                small_delay_delete[small_heap.top()]--;
                small_heap.pop();
            }
        }
        
    };
    class Solution {
    public:
        int longestSubarray(vector<int> &nums, int limit) {
            Heap heap;
            int maxLen = 0;
            int left=0,right=0;
            while (right<nums.size()){
                heap.add(nums[right++]);
                while (heap.limit()>limit){
                    heap.remove(nums[left++]);
                }
                maxLen = max(maxLen,right-left);
            }
            return maxLen;
        }
    };
    

    image-20210221113142497

    ac,但是效率极差,考虑到每次加入都需要logn的时间,思考能否用队列来实现

    使用单调队列

    class MQ{
        // 大队列
        deque<int> big_queue;
        // 小队列
        deque<int> small_queue;
    public:
        void add(int n){
            while ((!big_queue.empty())&&big_queue.back()<n){
                big_queue.pop_back();
            }
            big_queue.push_back(n);
            while ((!small_queue.empty())&&small_queue.back()>n){
                small_queue.pop_back();
            }
            small_queue.push_back(n);
    
        }
        int limit(){
            return big_queue.front()-small_queue.front();
        }
        void remove(int n){
            if(big_queue.front()==n){
                big_queue.pop_front();
            }
            if(small_queue.front()==n){
                small_queue.pop_front();
            }
        }
    
    };
    class Solution {
    public:
        int longestSubarray(vector<int> &nums, int limit) {
            MQ mq;
            int maxLen = 0;
            int left=0,right=0;
            while (right<nums.size()){
                mq.add(nums[right++]);
                while (mq.limit()>limit){
                    mq.remove(nums[left++]);
                }
                maxLen = max(maxLen,right-left);
            }
            return maxLen;
        }
    };
    

    值得注意的是,这种单调队列ADT用的是两个双向队列deque,分别维护窗口内包含right指向数据的一个单调递减、单调递增队列

    因为这个ADT的目的只是为了获取limit,所以无需记录每个数

    adt_1438

    image-20210221113232103

    效率明显变高了

  • 相关阅读:
    iOS 成员变量和属性的区别
    程序如何在RAM ROM运行,内存分配与分区
    iOS CGAffineTransform你了解多少?
    iOS配置字符表,查看Crash
    苹果开发账号过期不续费会怎样?
    iOS蓝牙接收外设数据自动中断
    数据结构简述
    antd按需加载,配置babel-plugin-import插件,编译后报错.bezierEasingMixin()解决方案
    docker 启动 nginx 服务
    安装docker
  • 原文地址:https://www.cnblogs.com/cpaulyz/p/14425314.html
Copyright © 2011-2022 走看看