基于环形缓冲区的deque实现方法

来源:互联网 发布:matlab中值滤波算法 编辑:程序博客网 时间:2024/05/29 03:17

众所周知,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
欢迎有兴趣的筒子学习研究,如有不当的地方,敬请批评指正。

0 0
原创粉丝点击