[仓颉Cangjie刷题模板] 单调队列(含数组版双端队列实现)

@[TOC]([仓颉Cangjie刷题模板] 单调队列(含数组版双端队列实现) )

一、 算法&数据结构

1. 描述

单调队列通常用来维护窗口移动、扩展过程中,当前窗口最大/最小值的问题。
单调队列是一种贪心算法,可以将很多O(nlgn)的算法优化成O(n)

2. 复杂度分析

  1. 总体 O(n),每次查询均摊O(1)

3. 常见应用

  1. 滑动窗口的最大值
  2. 多重背包的单调队列优化

4. 常用优化

  1. 由于仓颉里未直接提供deque,而使用LinkList代替是链表模型,无法提供随机访问的能力(虽然也不需要),于是我手写了一版基于数组的实现。以Pythonic的方式提供了下标访问的功能。

二、 模板代码

1. 询问滑窗最大值

在这里插入图片描述

例题: 239. 滑动窗口最大值
单调队列经典滑窗。
维护一个单调递降队列,队首是答案,队尾判断是否弹出。
窗口滑动时,判断队尾比i小的全部弹出,因为他们对后边的窗口来说贡献不会大于i。
滑动后判断队首元素是否窗外,弹出后,队首就是答案。
参考: [LeetCode解题报告] 滑动窗口最大值

package play_with_cangjie.lc.lc239
import std.collection.*

/**
 * Deque双端队列,基于线性表实现,可以实现O(1)随机访问 
 因此这里用lambda表达式的构造方法  
 注意使用front/back方法命名,而不是first/last(和collection冲突,返回迭代器)
 实现了更Pythonic的下标访问(index∈[-n..n-1]均合法,q[0]代表front,q[-1]代表back)
*/
class Deque<T> <: Collection<T> {
    var Size = 0
    public prop size: Int {
        get() {Size}
    }
    protected var head = 0
    // var _arr = Array<Option<T>>(1,repeat:Option<T>.None)  
    protected var _arr = Array<Option<T>>(1,{_=>Option<T>.None})    
    public init() { }
    public init(items: Collection<T>) {
        if (items.size >  _arr.size) {  // 提前申请空间
            reserve(items.size)
        }
        for (v in items) {
            this.append(v)
        }    
    }
    public func clear() {
        Size = 0
    }
    public func isEmpty() {
        return Size == 0
    }
    public func reserve(capacity: Int64) {
        if (capacity <= _arr.size) {
            return
        }
        // let arr = Array<Option<T>>(capacity,repeat:Option<T>.None)
        let arr = Array<Option<T>>(capacity,{_=>Option<T>.None})
        for (i in 0..Size) {
            arr[i] = _arr[(this.head + i) % _arr.size]
        }
        head = 0
        _arr = arr
    }
    public func front():T {
        if (Size == 0) {
            throw Exception("deque empty!")
        }
        return _arr[head].getOrThrow()
    }
    public func back():T {
        if (Size == 0) {
            throw Exception("deque empty!")
        }
        return _arr[(head + size+ _arr.size- 1 ) % _arr.size].getOrThrow()
    }
    public func popleft():T {
        if (Size == 0) {
            throw Exception("deque empty!")
        }
        let v = _arr[head].getOrThrow()
        head = (head + 1) % _arr.size
        Size--
        return v
    }
    public func pop():T {
        if (size == 0) {
            throw Exception("deque empty!")
        }
        let v = _arr[(head + Size+ _arr.size - 1 ) % _arr.size].getOrThrow()
        Size--
        return v
    }
    public func append(v:T) {
        if (Size == _arr.size) {
            reserve(Size<<1)
        }
        _arr[(head+Size)%_arr.size] = Option<T>.Some(v)
        Size++
    }
    public func appendleft(v:T) {
        if (Size == _arr.size) {
            reserve(size<<1)
        }        
        head = (head+_arr.size-1)%_arr.size
        _arr[head] = Option<T>.Some(v)
        Size++
    }
    public func at(i:Int64): T {
        if (i< -Size || Size<=i) {
            throw Exception("index out of range ${i} for size ${Size}")
        }
        return _arr[(head+_arr.size+(Size+i)%Size)%_arr.size].getOrThrow()
    }
    public operator func [](i: Int64): T {
        if (i< -Size || Size<=i) {
            throw Exception("index out of range ${i} for size ${Size}")
        }           
        return _arr[(head+_arr.size+(Size+i)%Size)%_arr.size].getOrThrow()
    }
    
    public func toArray() {
        return Array<T>(Size, {i=>_arr[(head+i)%_arr.size].getOrThrow()})
    }
    public func iterator(): Iterator<T> {
        DequeIterator(this)
    }
 
}
extend<T> Deque<T> <: ToString where T <: ToString {
    public func toString() {
        return toArray().toString()
    }
}
public class DequeIterator<T> <: Iterator<T> {
    var cur = 0
    var data = Option<Deque<T>>.None
    init(data: Deque<T>) {
        this.data = data
        cur = 0
    }
    public func next(): Option<T> {
        let dq = data.getOrThrow()
        if (cur >= dq.size) {
            return Option<T>.None
        }
        let ret = dq._arr[(dq.head + cur)%dq._arr.size]
        cur++
        return ret
    }
    // public func iterator(): Iterator<T>{
    //     return this
    // }
}
public class Solution {
    public func maxSlidingWindow(nums: Array<Int64>, k: Int64): Array<Int64> {
        
        let q = Deque<Int64>([])
   
        let ans = ArrayList<Int64>()
        for (i in 0..nums.size) {
            let v = nums[i]
            while (!q.isEmpty() && i-q[0]>=k) {
                q.popleft()
            }
            while(!q.isEmpty() && v >= nums[q[-1]]) {
                q.pop()
            }
            q.append(i)
            if (i>=k-1){
                ans.append(nums[q[0]])
            }                        
        }  
        
        return ans.toArray()        
    }
}

三、其他

  1. 单调队列和单调栈是强力优化,就是不容易想到。

四、更多例题

五、参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值