算法竞赛第一章-队列

1、队列

const int N=1e5;    //定义队列大小
int que[N], head,tail;  //队头队尾指针,队列大小为tail-head+1
//head++;    弹出对头,head<=tail
//queue[head];  //读对头数据
//que[++tail] = data;   //数据data入队,尾指针加1,注意不能溢出

2、STL queue

  1. queue <Type>q:定义队列,Type为数据类型,如int、float、char等
  2. q.push(item):把item放进队列
  3. q.front():返回队首元素,但不会删除
  4. q.pop():删除队首元素
  5. q.back():返回队尾元素
  6. q.size():返回元素个数
  7. q.empty():检查队列是否为空

P1540 [NOIP2010 提高组] 机器翻译

#include <bits/stdc++.h>

using namespace std;

int Hash[1003]={0};
queue<int>mem;

void solve(){
    int m, n; cin>>m>>n;
    int cnt=0;  //查词典的次数
    while(n--){
        int en; cin>>en;    //输入一个英文单词
        if(!Hash[en]){  //如果内存中没有这个单词
            ++cnt;
            mem.push(en);   //单词入队列,放到队列尾部
            Hash[en]=1; //记录内存中有这个单词
            while(mem.size()>m){    //内存满了
                Hash[mem.front()]=0;    //从内存中去掉这个单词
                mem.pop();  //从对头去掉
            }
        }
    }
    cout<<cnt<<"\n";
    return;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

3、手写循环队列

循环队列的结构体

#define N 1003
int Hash[N]={0};

struct myqueue{
    int data[N];
    int head, rear;
    bool init(){
        head=rear=0;
        return true;
    }
    int size(){return (rear-head+N)%N;}
    bool empty(){
        if(size()==0)   return true;
        else    return false;
    }
    bool push(int e){
        if((rear+1)%N==head)    return false;
        data[rear]=e;
        rear=(rear+1)%N;
        return true;
    }
    bool pop(int &e){
        if(head==rear)  return false;
        e=data[head];
        head=(head+1)%N;
        return true;
    }
    int front(){return data[head];}
}Q;

例题的完整代码

#include <bits/stdc++.h>

using namespace std;

#define N 1003
int Hash[N]={0};

struct myqueue{
    int data[N];
    int head, rear;
    bool init(){
        head=rear=0;
        return true;
    }
    int size(){return (rear-head+N)%N;}
    bool empty(){
        if(size()==0)   return true;
        else    return false;
    }
    bool push(int e){
        if((rear+1)%N==head)    return false;
        data[rear]=e;
        rear=(rear+1)%N;
        return true;
    }
    bool pop(int &e){
        if(head==rear)  return false;
        e=data[head];
        head=(head+1)%N;
        return true;
    }
    int front(){return data[head];}
}Q;

void solve(){
    Q.init();
    int m,n;    cin>>m>>n;
    int cnt=0;
    while(n--){
        int en; cin>>en;
        if(!Hash[en]){
            ++cnt;
            Q.push(en);
            Hash[en]=1;
            while(Q.size()>m){
                int tmp;    Q.pop(tmp);
                Hash[tmp]=0;
            }
        }
    }
    cout<<cnt<<'\n';
    return;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

4、 双端队列

双端队列是一种不是很规矩的队列,双端队列是一种具有队列和栈性质的数据
手写数组模拟

#define N 1003
int queue[N], head, rear;   //对头队尾指针,队列大小为tail-head+1
//弹出对头head++;
//queue[--head]=data;   //数据入对头,注意不能溢出
//q[head];  //读队头数据
//tail--;   //弹出队尾
//que[++tail]=data; //数据入队尾,注意也不能溢出

stl中的dequeue

deque<int>dq;
//dq[i]:返回队列中下标为i的元素
//dq.front():返回队头
//dq.back():返回队尾
//dq.pop_back():删除队尾,不返回值
//dq.pop_front():删除队头,不返回值
//dq.push_back(e):在队尾添加一个元素e
//dq.push_front(e):在对头添加一个元素e

滑动窗口
P1186洛谷传送门

#include <bits/stdc++.h>

using namespace std;

const int N=1000005;
int a[N];
deque<int>q;

void solve(){
    int n,m;    cin>>n>>m;
    for(int i=1;i<=n;++i)   cin>>a[i];
    for(int i=1;i<=n;++i){
        while(!q.empty()&&a[q.back()]>a[i])  q.pop_back();
        q.push_back(i);
        if(i>=m){
            while(!q.empty()&&q.front()<=i-m)   q.pop_front();
            cout<<a[q.front()]<<' ';
        }
    }
    cout<<'\n';
    while(!q.empty())   q.pop_front();
    for(int i=1;i<=n;++i){
        while(!q.empty()&&a[q.back()]<a[i])  q.pop_back();
        q.push_back(i);
        if(i>=m){
            while(!q.empty()&&q.front()<=i-m)   q.pop_front();
            cout<<a[q.front()]<<' ';
        }
    }
    cout<<'\n';
    return;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

5、单调队列

单调队列解决有长度限制的最大子段和问题
image
image

#include <bits/stdc++.h>

using namespace std;

const int N=1000005;
int s[N];
deque<int>dq;

void solve(){
    int n,m;    cin>>n>>m;
    for(int i=1;i<=n;++i)   cin>>s[i];
    for(int i=1;i<=n;++i)   s[i]=s[i]+s[i-1];   //计算前缀和
    int ans=-1e8;
    dq.push_back(0);
    for(int i=1;i<=n;++i){
        while(!dq.empty()&&dq.front()<i-m)  dq.pop_front();
        if(dq.empty())  ans=max(ans,s[i]);
        else    ans=max(ans, s[i]-s[dq.front()]);   //对头就是最小的s[k]
        while(!dq.empty()&&s[dq.back()]>=s[i])  dq.pop_back();  //队尾大于s[i],去尾
        dq.push_back(i);
    }
    cout<<ans<<'\n';
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    solve();
    return 0;
}

P1440 求m区间内的最小值传送门

#include <bits/stdc++.h>

using namespace std;

const int N=2e6+10;
deque<int>dq;
int a[N];

void solve(){
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;++i)   cin>>a[i];
    for(int i=1;i<=n;++i){
        if(i==1){
            cout<<0<<'\n';
            dq.push_back(1);
            continue;
        }
        cout<<a[dq.front()]<<'\n';
        while(!dq.empty()&&a[i]<=a[dq.back()]) dq.pop_back();
        while(!dq.empty()&&i-1-m+1>=dq.front()) dq.pop_front();
        dq.push_back(i);
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    solve();
    return 0;
}
posted @ 2024-05-07 22:56  cxy8  阅读(9)  评论(0编辑  收藏  举报