双端队列(deque)的应用

问题描述:(滑动最小值)

 给定的一个长度为n的数列a0,a1,a2..an-1,和一个整数k,求数列bi=min(ai,ai+1..ai+k-1){i=0,1,..n-k}
 {1<=k<=n<=1e6;0<=ai<=1e9}
 sample input
 n=5
 k=3
 a={1,3,5,4,2}
 sample ouput
 b={1,3,2}
【分析】:
    solution 1:
        RMQ :复杂度O(nlogn)
    solution 2:
        deque <双端队列:可以在头部和尾部插入和删除元素的数据结构> 在O(n)时间内解决
【思路】:
    最开始时双端队列为空,然后不断维护双端队列使它按照如下顺序,注意保存的是后面的最小值的a数组的元素下标

    设双端队列从头部开始的元素为值为xi,则xi<xi+1 && a(xi)<a(xi+1)
    首先,为了计算b0,把0到 k-1 依次加入队列,在加入i时,当双端队列的末尾的值j满足aj>=ai,则不断取出
    直到双端队列为空或者aj<ai之后再在末尾加入i
    
    等到 k-1 都加入到队列里面,查看队列头部的值j,那么b0=aj,如果j==0,那么在之后的计算不会用到,所以删去
    接下来,为了计算bi,需要在队列的末尾加入k, 不断加入元素,就可以计算后面的bi的值,由于双端队列的加入和       删除都进行了O(n)次,因此算法复杂度O(n)
    对于样例,队列模拟如下:
    add 0 -> {0} (存的是下标)
    add 1 -> {0,1}
    add 2 -> {0,1,2}
    b0=a0 =1;
    remove 0 -> {1,2}
    add 3 -> {1,3} (a2>=a3,因此删除2)
    b1=a1=3;
    remove 1 -> {3}
    add 4 -> {4}  (a3>=a4,因此删除3)
    b2=a4=2
代码实现:
 

//输入
int n,k;
int a[maxn],b[maxn];
int deq[maxn];//双端队列
void solve()
{
    int s=0,t=0; //双端队列的头部和末尾
    for(int i=0; i<n; ++i)
    {
        //在双端队列的末尾加入i
        while(s<t&&a[deq[t-1]]>=a[i]) t--;
        deq[t++]=i; 
        if(i-k+1>=0)
        {
            b[i-k+1]=a[deq[s]];
            if(deq[s]==i-k+1) s++;//从双端队列的头部删除元素
        }
    }
    for(int i=0; i<=n-k; ++i)
    {
        printf("%d%c",b[i],i==n-k?'\n':' ');
    }
}


### 双端队列 Deque 数据结构 #### 定义 双端队列 (Deque, Double-ended Queue) 是一种可以从两端进行插入和删除操作的线性数据结构[^1]。这种特性使得它可以灵活地处理多种类型的队列需求。 #### 基本操作 双端队列支持如下基本操作: - `push_front(x)`:在前端插入元素 x。 - `pop_front()`:移除并返回前端元素。 - `push_back(x)`:在后端插入元素 x。 - `pop_back()`:移除并返回后端元素。 - `empty()`:判断双端队列是否为空。 - `size()`:获取当前存储元素的数量。 这些操作的时间复杂度通常为 O(1),这得益于内部采用的高效实现机制。 #### 实现方式 双端队列可以通过两种主要的方式实现——基于数组或者链表。每种方法都有各自的优劣之处: - **数组实现**:通过动态分配连续内存来模拟双端队列的行为,适合于已知大小范围的情况;但在频繁增删时可能会引发大量的内存复制开销。 - **链表实现**:利用节点之间的指针链接形成双向循环列表,允许任意位置快速插入/删除而不必担心内存重定位问题,更适合未知规模的数据集。 对于 C++ 中的标准库容器 `std::deque` 而言,则采用了分段管理的技术方案,在保持随机访问特性的前提下优化了性能表现[^2]。 然而值得注意的是,由于 `std::deque` 的特殊设计,当涉及到大量遍历操作时,其效率相对较低,因为每次迭代都需要额外检查是否跨越了某个小块区域的边界[^3]。 #### 应用场景 尽管存在上述局限性,双端队列仍然有着广泛的应用领域: - 作为标准模板库(STL)中栈(stack)队列(queue)的基础实现之一; - 处理需要同时维护最新与最旧记录的任务调度算法; - 缓存淘汰策略中的 LRU(Least Recently Used)缓存管理; - 图形界面应用程序的消息传递系统等[^4]。 ```cpp // 创建一个简单的 C++ std::deque 并执行一些基础操作 #include <iostream> #include <deque> int main() { std::deque<int> d; // 插入几个整数到 deque 尾部 for(int i=0; i<5; ++i){ d.push_back(i); } // 输出所有元素 while(!d.empty()){ std::cout << d.front() << ' '; d.pop_front(); } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值