基於環形緩衝區的deque實現方法

衆所周知,C++ STL中有一個叫做deque的容器,實現的是雙端隊列數據結構,這種隊列允許同時從隊列的首部和尾部插入和刪除數據。
然而在STL中這種數據結構是用”分段連續”的物理結構實現的(可以參考侯捷老師的《STL源碼剖析》)。網上分析STL中這種容器的文章很多,如:http://blog.csdn.net/baidu_28312631/article/details/48000123 (STL源碼剖析——deque的實現原理和使用方法詳解)就分析得很清楚。
個人認爲STL的這種實現方法略有畫蛇添足的嫌疑,不光增加了空間管理複雜度,而且使得iterator的隨機訪問變得低效(相對vector的iterator)。
侯捷老師在《STL源碼剖析》第143頁也提到:“對deque進行的排序操作,爲了最高效率,可將deque先完整複製到一個vector身上,將vector排序後(利用STL sort算法),再複製回deque。”只因deque的Randon Access Iterator遠不如vector的原生指針來得高效。
雖然作者暫時沒弄明白爲什麼需要對“隊列”做排序操作,即便如此,侯捷老師指出的copy-sort-copy思路,作者也是難苟同。

下面開始引出本文的主角——基於環形緩衝區的deque實現方法。
由於deque需要允許從隊列頭部插入和刪除數據,如果像vector那樣,爲了在頭部增刪數據,每次都需要移動整個列表,顯然是無法忍受的。
然而,利用環形鏈表思想,將vector的緩衝區看做是一個環形,則可以完美的在像vector一樣連續的可增長的連續存儲空間內實現deque。
數據結構定義如下:

class deque{
    // pointer to range of storage array.
    //full storage is [_Myfirst, _Mylast)
    //buffer is cycle, that is to say, next(_Mylast-1)=_Myfirst, prev(_Myfirst)=_Mylast-1
    pointer _Myfirst, _Mylast;

    //head and tail of deque
    //real data is [_Myhead,_Mytail)
    //so if tail<head, data is [_Myhead, _Mylast-1, _Myfirst, _Mytail)
    //head points to the first elem  available for read
    //tail points to the first space available for write
    pointer _Myhead, _Mytail;
}

其中[_Myfirst, _Mylast)記錄了當前緩衝區的地址範圍(即當前允許的最大隊列長度)。
_Myhead和_Mytail記錄了當前隊列的頭部和尾部的位置。
由於緩衝區是被看做是環形的,所以數據[_Myhead,_Mytail)可能有兩種情況:
1. _Mytail >= _Myhead, 隊列數據在[_Myhead,_Mytail)
2. _Mytail < _Myhead, 隊列數據在[_Myhead, _Mylast-1, _Myfirst,_Mytail)

下面來詳細講述deque的4個操作:

void push_front(const value_type &_Val){
    _Myhead = _Prev(_Myhead);
    _STDEXT unchecked_uninitialized_fill_n(_Myhead, 1, _Val, this->_Alval);
    if (_Myhead == _Mytail){//buffer full
        _Buy(2*capacity());
    }
}

void push_back(const value_type &_Val){
    _STDEXT unchecked_uninitialized_fill_n(_Mytail, 1, _Val, this->_Alval);
    _Mytail = _Next(_Mytail);
    if (_Myhead == _Mytail){//buffer full
        _Buy(2*capacity());
    }
}

bool pop_front(){
    if (empty()){
        return false;
    }
    _Destroy(_Myhead);
    _Myhead=_Next(_Myhead);

    return true;
}

bool pop_back(){
    if (empty()){
        return false;
    }
    _Mytail = _Prev(_Mytail);
    _Destroy(_Mytail);
    return true;
}

bool empty() const{
    return _Myhead == _Mytail;
}

特別說明,當_Myhead == _Mytail的時候,表示隊列爲空。
可以看到,從頭部push和pop的時候,實際只需要將_Myhead- -和_Myhead++
同理,從尾部push和pop的時候,只需要_Mytail++和_Mytail- -
當插入數據後,如果_Myhead==_Mytail,表示緩衝區已滿,需要重新申請更大(2倍)的緩衝區,並把隊列數據拷貝到新空間。

可以看到,以上代碼在跟vector類似的連續空間上簡單的實現了deque的所有關鍵操作,更讓人欣慰的是,iterator(如果需要)是跟vector一樣的原生指針,要在上面實現sort算法將是相當高效的,絕對不需要的copy-sort-copy。

我將代碼放在了這裏:
https://github.com/vipally/clab/blob/master/stl/deque/deque.hpp
歡迎有興趣的筒子學習研究,如有不當的地方,敬請批評指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章