1. 程式人生 > >雙端單調佇列

雙端單調佇列

這次介紹一種新的資料結構:雙端佇列:雙端佇列是指允許兩端都可以進行入隊和出隊操作的佇列,其元素的邏輯結構仍是線性結構。將佇列的兩端分別稱為前端和後端,兩端都可以入隊和出隊。

堆疊、佇列和優先佇列都可以採用雙端佇列來實現

本文介紹單調雙端佇列的原理及應用。

單調佇列,顧名思義,就是一個元素單調的佇列,那麼就能保證隊首的元素是最小(最大)的,從而滿足最優性問題的需求。

給定一個長度為n的數列,一個k,求所有的min(ai,ai+1.....ai+k-1),i=0,1,....n-k

通俗一點說就是一個長度固定的滑動的視窗,求每個視窗內的最小值。

你當然可以暴力求解,依次遍歷每個視窗.

介紹單調佇列用法:我們維護一個單調佇列

單調佇列呢,以單調遞增序列為例:

1、如果佇列的長度一定,先判斷隊首元素是否在規定範圍內,如果超範圍則增長隊首。

2、每次加入元素時和隊尾比較,如果當前元素小於隊尾且佇列非空,則減小尾指標,隊尾元素依次出隊,直到滿足佇列的調性為止

我們說演算法的優化就是重複計算過程的去除。

按視窗一次次遍歷就是重複計算。最值資訊沒有利用好。

我們為什麼可以這麼維護?

首先,遍歷到的元素肯定在佇列元素之後。

其次,如果當前元素更小的話。

頭部的值比當前元素大,頭部還比當前元素先過期。所以以後計算再也不會用到它了。我們可以放心的去掉它。

下面給出程式碼和解釋

int n,k;//長度為n的數列,視窗為k
int a[MAX_N];//數列
int b[MAX_N];//存放
int deq[MAX_N]//模擬佇列

void solve()
{
    int s = 0,t = 0;//頭和尾
    for(int i=0;i<n;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++;
    }
}

基本入門就到這裡。