zoukankan      html  css  js  c++  java
  • 双端队列 deque 模板 && 滑动窗口 (自出)

    deque 即为双端队列,是c++语言中STL库中提供的一个东西,其功能比队列更强大,可以从队列的头与尾进行操作...

    但是它的操作与队列十分相似,详见代码1:

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <deque>
     4 //实际上,引用queue头文件也可以,里面包含了deque头文件
     5 
     6 using namespace std;
     7 
     8 deque<int> dq; //定义一个储存整型变量的双端队列dq
     9 
    10 int main() {
    11     dq.push_back(1); //从队列后插入,此时dq:1 (下同)
    12     dq.push_back(2); //1,2
    13     dq.push_front(1); //从队列前插入,  1,1,2
    14     dq.push_front(2); //2,1,1,2
    15     cout << dq.front() << endl; //输出队首元素,结果为2
    16     cout << dq.back() << endl; //输出队尾元素,结果为2
    17     dq.pop_front(); //弹出队首元素 1,1,2
    18     dq.pop_back(); //弹出队尾元素 1,1
    19     dq.clear(); //清空操作 
    20     if (dq.empty()) cout<<"队列已空!"<<endl; //判断队列是否为空
    21     cout << dq.size() << endl; //结果为0,即输出队列中元素个数
    22     return 0;
    23 }
    deque 模板

    下面便是一个双端队列的模板题....先看题面:

    滑动窗口求最值

    题目描述:

      在一个长度为n的整数序列上有一个长度为k的滑动窗口,求滑动窗口内的最大值。

    输入输出:

      输入n,k (n <= 10000,k <= n)

      输出第_个滑动窗口以及此滑动窗口中的最大值...

    题目解析:

      就是在一个序列上对于每个长度为k的区间,求区间内的最值。

      一种朴素的做法是,枚举区间起点,再自此向后比较k个元素,找出最值,这样的复杂度是O(nk)的。

      还有一种不错的做法是利用单调队列。不妨假设我们已经得到了一个单调队列,他维护了当前的滑动窗口,显然,队首元素就是窗口内的最值。现在再来考虑如何用单调队列维护滑动窗口(以最大值为例,队列则为单调递减的,队首元素为窗口内的最大值):

      我们遍历序列的每个元素,当队列为空时,肯定要加入队列;队列不为空,就要先从队尾弹出较小的元素,再加入,保证队列单调(这里有一个有趣的类比,也是滑动窗口的主要思想:如果一位OIer比你年轻还比你强,那你就没法超越他了);但滑动窗口是有长度限制的,怎么考虑呢?我们可以保存每个元素的序号,当发现队首元素的序号与当前考虑元素相比,已经出了滑动窗口,就弹出队首元素。

      因为每个元素都只会进入队列一次且只会离开队列一次,可以认为时间复杂度是O(n)的。

    下面请见std 外加详解:

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <deque>
     4 
     5 using namespace std;
     6 
     7 struct num { //定义结构体存储元素的序号(id)及值(value)
     8     int id, value;
     9 
    10     num(int i, int v) : id(i), value(v) {} //构造关于结构体的函数,这样在后面的操作时比较简单 
    11 };
    12 
    13 int n, k, a[10005], first = 1;
    14 
    15 deque<num> dq;
    16 
    17 int main() {
    18     cin >> n >> k;
    19     for(int i = 1; i <= n; ++i) {
    20         scanf("%d", &a[i]);
    21         if (dq.empty()) dq.push_back(num(i, a[i]));
    22         //若队列为空,则直接进入队列
    23         else {
    24             num f = dq.front(); //取队首元素(包括了id和value) 
    25             if (i > f.id + k - 1) dq.pop_front();
    26             //若队首元素的序号距当前元素太远(超出窗口长度)则弹出队首元素,相当于模拟一个窗口右滑的操作 
    27             num b = dq.back(); //取队尾元素
    28             while (b.value < a[i]) { //若队尾元素小于当前元素则弹出队尾
    29                 dq.pop_back();
    30                 if (dq.empty()) break; //注意!队列为空则不能继续弹出,注意细小的边界 
    31                 b = dq.back();
    32             }
    33             dq.push_back(num(i, a[i])); //将当前元素放入队列中合适位置  
    34             //如果不进行第10行的操作,则需要这样写:num qaq;  qaq.id=i;  qaq.value=a[i]; 此语句等价于第十行 
    35         }
    36         if (i >= k) { //当考虑的元素个数达到窗口长度时,开始输出
    37             if (first) first = 0;
    38             else printf("
    ");//这种操作很鬼畜,会很好的将每次的答案进行换行且第一行被很好地跳过换行 
    39             num f = dq.front();//取队首元素  
    40             printf("第%d个滑动窗口的最大值为%d", i-k+1, f.value);
    41         } //事实上,对于长度为n的序列,长度为k的窗口,共有n-k+1个不同的窗口
    42     }
    43     return 0;
    44 }
    滑动窗口

    关于滑动窗口这个题,稍微有点饶脑,但请记住上文中的红色+下滑线那句话即可理解...

  • 相关阅读:
    Java中不定参的使用规则
    关于泛型中<T extends comparable>的理解
    Java泛型的定义以及对于<? extends T>和<? super T>
    spring入门(一)
    谷歌游览器对<input type='file'> change只能响应1次解决和样式的改变
    .net资源文件及卫星程序集使用
    datetimekind.unspecified理解
    ILMerge工具
    .net 数据库抽象类
    格式相关类
  • 原文地址:https://www.cnblogs.com/New-ljx/p/10419099.html
Copyright © 2011-2022 走看看