max_queue 最大値をいち早く返すデータ構造

ちょっとマニアックなデータ構造を紹介

その名もmax queue、使い勝手はほとんどFIFOなqueueと一緒で、enqueue()もdequeue()もO(1)だけどそれに加えて「入ってるデータのうち最大の値」もO(1)の計算量で算出するという代物。
兄弟分で最小の物を返すmin queueや、両方の機能を備えたmin-max queueもあるけどmax queueが判れば自明なので割愛。

最大の値を覚えておけば良いだけに思えるけど、最大の値がdequeueされてしまった場合にO(n)の時間を掛けて再び探索してたら条件を満たせない。

max-queueでは最大の値を覚えておくため専用のqueue(以後最大値queueと呼ぶ)を内部でもう一本持つ。

この青い線がqueueに入ってるデータ列で高さがデータの大きさを表す、下に書かれている薄い線の入ったデータ列が最大値queue
こちらの最大値queueでは過去に入れられたデータのうち、一番大きな物から順番に降順にデータを記録し、他は無視している。
薄い線で書かれたデータは最大値queueの側では保持していない。

複雑そうに見えるけど
enqueueする時に「挿入したいデータよりも小さいデータが最大値dequeueの末尾にあったらそれらを消してから入れる」だけ。
dequeueする時に一緒にその値が出ていくなら最大値queueからも同時にdequeueするだけ。
コードで書くと非常に明快。

https://gist.github.com/3847509

#include <list>

template <typename T>
struct max_queue {
    max_queue():queue_(),max_(){}
    void enqueue(const T& n){
        queue_.push_back(n);
        const T& new_item = queue_.back();
        while(!max_.empty() && *max_.back() < new_item){ // 最新の値より小さい末尾を全部消す
            max_.pop_back();
        }
        max_.push_back(&new_item);
    }
    T dequeue(){
        if(queue_.empty()){ throw std::exception(); }
        const T& deleting_item = queue_.front();
        const T ans = queue_.front(); // copy
        if(max_.front() == &deleting_item){ // 一緒に最大値が出ていくなら一緒に消す
            max_.pop_front();
        }
        queue_.pop_front();
        return ans;
    }
    const T& max()const{
        if(max_.empty()){ throw std::exception(); }
        return *max_.front(); // 最大値dequeueの先端を返すだけ。速い。
    }
private:
    std::list<T> queue_; // 普通のqueue
    std::list<const T*> max_;// 最大値queue
};

#include <assert.h>
#include <iostream>
int main(void){
    max_queue<int> q;
    q.enqueue(1); assert(q.max()==1);
    q.enqueue(3); assert(q.max()==3);
    q.enqueue(5); assert(q.max()==5);
    q.enqueue(2); assert(q.max()==5);
    q.enqueue(7); assert(q.max()==7);
    q.enqueue(4); assert(q.max()==7);
    q.enqueue(1); assert(q.max()==7);
    q.enqueue(2); assert(q.max()==7);
    q.enqueue(1); assert(q.max()==7);
    assert(q.dequeue()==1); assert(q.max()==7);
    assert(q.dequeue()==3); assert(q.max()==7);
    assert(q.dequeue()==5); assert(q.max()==7);
    assert(q.dequeue()==2); assert(q.max()==7);
    assert(q.dequeue()==7); assert(q.max()==4);
    assert(q.dequeue()==4); assert(q.max()==2);
    assert(q.dequeue()==1); assert(q.max()==2);
    assert(q.dequeue()==2); assert(q.max()==1);
    assert(q.dequeue()==1);
}

でも使いどころは割と難しいと思う。