从零开始写STL-容器-双端队列

Posted joeylee97

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始写STL-容器-双端队列相关的知识,希望对你有一定的参考价值。

从零开始写STL-容器-双端队列

什么是双端队列?在介绍vector源码,我们发现在vector前端插入元素往往会引起大量元素的重新分配,双端队列(deque)就是为了解决这一问题,双端队列中在首端和末端插入元素的时间复杂度都为O(1),也许你会说链表不行吗,但是其实链表存在一定的缺陷,比如每个结点都要多花出两份存储指针的空间,下面我们将通过源码来分析deque的实现。

基本实现模型 链状数组

动态数组中在首端插入元素低效率的根本原因在于不能在首端分配新的内存,结合链表的实现我们可以实现一个链状数组,其中维护一个内存缓存数组,数组中每个元素指向一片固定大小的内存块,当前内存块耗尽(到达最末端或者首端)就会通过分配器来调度迭代器的位置进入下一个或者前一个内存块。
要维护一个双端队列需要实现哪些?

  • 内存缓冲区数组的位置
  • 当前已使用的缓冲区大小和位置(通过 维护数组中两个指针,一个指向被使用的最前面的内存块,一个指向被使用的最后面的内存块。

双端队列迭代器

要维护一个迭代器需要实现哪些东西?

  • 首先需要知道当前在哪个内存块中
  • 还要知道内存块的开始/结束位置 以免访问未初始化的内存
template<class T, size_t Bufsize = 0>
    struct _deque_iterator :  public random_access_iterator<T>
    {
        typedef _deque_iterator<T> iterator;
        // 这里计算的是一个内存块中可以存放多少T类型的实例
        static size_t buffer_size()
        {
            return _deque_buf_size(Bufsize, sizeof(T));
        }
        
        _deque_iterator()
        {
            cur = first = last = NULL;
            node = NULL;
        }
        T* cur;//此迭代器所指缓冲区中的元素
        T* first;//缓冲区开头元素
        T* last;//缓冲区尾部元素
        map_pointer node;//缓冲区控制器

        void set_node(map_pointer new_node)
        {
            node = new_node;
            first = *new_node;
            last = first + difference_type(buffer_size());
        }

        reference operator*() const
        {
            return *cur;
        }
        pointer operator->() const
        {
            return &(operator*());
        }
        difference_type operator-(const self& x)const
        {
            return difference_type(buffer_size())*(node - x.node - 1) + (cur - first) + (x.last - x.cur);// 间隔内存块*每个内存块个数 + 结点offset
        }
        self& operator++()
        {
            ++cur;
            if (cur == last) //到当前内存块最后一个元素,由链状数组 内存分配器 来指向下一个内存块
            {
                set_node(node + 1);
                cur = first;
            }
            return *this;
        }
        self operator++(int)
        {
            self tmp = *this;
            ++*this;
            return tmp;
        }
        self& operator--()
        {
            --cur;
            if (cur == first)
            {
                set_node(node - 1);
                cur = last;
            }
            return *this;
        }
        self operator--(int)
        {
            self tmp = *this;
            --*this;
            return tmp;
        }
        //随机存取
        self& operator+=(difference_type n)
        {
            difference_type offset = n + (cur - first);
            if (offset >= 0 && offset < (difference_type)(buffer_size()))
                cur += n;//在当前的缓冲块内部
            else
            {
                difference_type node_offset = offset > 0 ? offset / difference_type(buffer_size())
                    : -difference_type((-offset - 1) / buffer_size()) - 1;
                set_node(node + node_offset);// 计算内存块的偏移量
                cur = first + (offset - node_offset*difference_type(buffer_size()));
            }
            return *this;
        }
        self operator+(difference_type n) const
        {
            self tmp = *this;
            return tmp += n;
        }

        self& operator-=(difference_type n)
        {
            return *this += -n;
        }
        self operator-(difference_type n)const
        {
            self tmp = *this;
            return tmp -= n;
        }
        reference operator[](difference_type n) const
        {
            return *(*this + n);
        }

        bool operator==(const self &x) { return x.cur == cur; }
        bool operator!=(const self &x)
        {
            return !(*this==x);
        }
        bool operator<(const self& x)
        {
            return (node == x.node) ? (cur < x.cur) : (node < x.node);//优先比较缓冲区!
        }
    };

双端队列源码

··· c++

template<class T,size_t Buf_size = 0>
class deque
{
public:
    typedef T value_type;
    typedef T* pointer;
    typedef T& reference;
    typedef size_t size_type;
    typedef _deque_iterator<T> iterator;
    typedef ptrdiff_t difference_type;
    typedef size_t size_type;
    static size_t buffer_size()
    {
        return _deque_buf_size(Buf_size, sizeof(T));
    }
protected:
    typedef pointer* map_pointer;
    iterator start, finish;
    map_pointer map;
    size_type map_size;
    std::allocator<T> data_allocator;
    std::allocator<pointer> map_allocator;
    void create_map_and_nodes(size_type num_elements)
    {
        // 所需要的内存块个数
        size_type num_nodes = num_elements / buffer_size() + 1;
        map_size = std::max((size_t)8, num_nodes + 2);
        map = map_allocator.allocate(map_size);
        //尽量从中间开始,给前端和后端留下扩展空间
        map_pointer nstart = map + (map_size - num_nodes) / 2;
        map_pointer nfinish = nstart + num_nodes - 1;
        map_pointer cur;
        try
        {
            //分配内存
            for (cur = nstart; cur <= nfinish; cur++)
                *cur = data_allocator.allocate(buffer_size());
        }
        catch (...)
        {

        }
        start.set_node(nstart);
        finish.set_node(nfinish);
        start.cur = start.first;
        finish.cur = finish.first + num_elements%buffer_size();
    }
    /*
    在内存中初始化元素
    */
    void fill_initialize(size_t n,const value_type& val)
    {
        create_map_and_nodes(n);
        map_pointer cur;
        try
        {
            for (cur = start.node; cur < finish.node; cur++)
                std::uninitialized_fill(*cur, *cur + buffer_size(), val);
            std::uninitialized_fill(finish.first, finish.last, val);
        }
        catch (...)
        {

        }
    }
    void reallocate_map(size_type nodes_to_add, bool add_at_front)
    {
        size_type old_num_nodes = finish.node - start.node + 1;
        size_type new_num_nodes = old_num_nodes + nodes_to_add;

        map_pointer new_start;
        if (map_size > 2 * new_num_nodes)
        {
            new_start = map + (map_size - new_num_nodes) / 2 + (add_at_front ? nodes_to_add : 0);
            if (new_start < start.node)
                std::copy(start.node, finish.node + 1, new_start);
            else
                std::copy_backward(start.node, finish.node + 1, new_start + old_num_nodes);
        }
        else
        {
            size_type new_map_size = map_size + std::max(map_size, nodes_to_add) + 2;
            map_pointer new_map = map_allocator.allocate(new_map_size);
            new_start = new_map + (new_map_size - new_num_nodes) / 2 + (add_at_front ? nodes_to_add : 0);
            std::copy(start.node, finish.node + 1, new_start);
            map = new_map;
            map_size = new_map_size;
        }
    }
    void push_front_aux(const value_type& val)
    {
        if (start.node - map < 1)
            reallocate_map(1, true);
        *(start.node - 1) = data_allocator.allocate(buffer_size());
        try
        {
            start.set_node(start.node - 1);
            start.cur = start.last - 1;
            data_allocator.construct(start.cur, val);
        }
        catch (...)// commit or rollback 异常安全
        {
            start.set_node(start.node + 1);
            start.cur = start.first;
            data_allocator.deallocate(*(start.node - 1),buffer_size());
            throw;
        }
    }
    void push_back_aux(const value_type& val)
    {
        if (map_size - (finish.node - map) < 2)
            reallocate_map(1, false);
        *(finish.node + 1) = data_allocator.allocate(buffer_size());
        try
        {
            data_allocator.construct(finish.cur, val);
            finish.set_node(finish.node + 1);
            finish.cur = finish.first;
        }
        catch (...)
        {
            data_allocator.deallocate(*(finish.node + 1),buffer_size());
        }
    }
public:
    //Construct
    deque() :start(), finish(), map(), map_size(0) 
    {
        fill_initialize(0, value_type());
    }
    deque(int n, const value_type& val)
    {
        deque();
        fill_initialize(n, val);
    }

    //Iterator
    iterator begin()
    {
        return start;
    }
    iterator end()
    {
        return finish;
    }
    reference operator[](size_type n)
    {
        return start[(difference_type)n];
    }
    reference front()
    {
        return *start;
    }
    reference back()
    {
        return *(finish - 1);
    }
    size_type size()
    {
        return finish - start;
    }
    size_type max_size() { return size_type(-1); }
    bool empty() { return finish == start; }
    //Modifiers
    void push_back(const value_type& val)
    {
        if (finish.cur != finish.last - 1)
        {
            data_allocator.construct(finish.cur, val);
            ++finish.cur;
        }
        else
            push_back_aux(val);
    }
    void push_front(const value_type& val)
    {
        if (start.cur != start.first)
        {
            data_allocator.construct(start.cur - 1, val);
            --start.cur;
        }
        else
            push_front_aux(val);
    }
    void clear()
    {
        for (auto it = start.cur + 1; it < finish.cur; it++)
            data_allocator.destroy(it);
        for (auto it = start.node + 1; it <= finish.node; it++)
            map_allocator.destroy(it),map_allocator.deallocate(it,1);
        finish = start;
    }
    void pop_back()
    {
        if (finish.cur != finish.first)
        {
            --finish.cur;
            data_allocator.destroy(finish.cur);
        }
        else
        {
            data_allocator.deallocate(finish.first,buffer_size());
            finish.set_node(finish.node - 1);
            finish.cur = finish.last - 1;
            data_allocator.destroy(finish.cur);
        }
    }
    void pop_front()
    {
        if (start.cur != start.last - 1)
        {
            data_allocator.destroy(start.cur);
            start.cur++;
        }
        else
        {
            destroy(start.cur);
            set_node(start.node + 1);
            start.cur = start.first;
        }
    }

    iterator erase(iterator pos)
    {
        iterator next = pos++;
        difference_type index = pos - start;
        if (index < size() / 2)
        {
            copy_backward(start, pos, next);
            pop_front();
        }
        else
        {
            copy(next, finish, pos);
            pop_back();
        }
        return start + index;
    }

    iterator erase(iterator first, iterator last)
    {
        if (first == start&&last == finish)
        {
            clear();
            return finish;
        }
        else
        {
            difference_type n = last - first;
            difference_type elems_before = first - start;
            if (elems_before < (size() - n) / 2)//如果前方的元素较少
            {
                copy_backward(start, first, last);
                iterator new_start = start + n;
                for (auto it = start; it < new_start; it++)
                    data_allocator.destroy(it);
                for (auto it = start; it < new_start; it++)
                    data_allocator.deallocate(it, buffer_size());
                start = new_start;
            }
            else
            {
                copy(last, finish, first);
                iterator new_finish = finish - n;
                for (auto it = new_finish; it < finish; it++)
                    data_allocator.destroy(it);
                for (auto it = new_finish; it < finish; it++)
                    data_allocator.deallocate(it, buffer_size());
                finish = new_finish;
            }
            return start + elems_before;
            
        }
    }

    iterator insert(iterator pos, const value_type& x)
    {
        if (pos.cur == start.cur)
        {
            push_front(x);
            return start;
        }
        else if (pos.cur == finish.cur)
        {
            push_back(x);
            return finish - 1;
        }
        else
        {
            difference_type index = pos - start;
            value_type x_copy = x;
            if (index < size() / 2)
            {
                push_front(front());
                iterator front1 = start;
                ++front1;
                iterator front2 = front1;
                ++front2;
                pos = start + index;
                iterator pos1 = pos;
                ++pos1;
                copy(front2, pos1, front1);

            }
            else
            {
                push_back(back());
                iterator back1 = finish;
                --back1;
                iterator back2 = back1;
                --back2;
                pos = start + index;
                copy_backward(pos, back2, back1);
            }
            *pos = x_copy;
            return pos;
        }
    }
};

}

```

以上是关于从零开始写STL-容器-双端队列的主要内容,如果未能解决你的问题,请参考以下文章

STL容器:deque双端队列学习

从零开始写STL—容器—vector

[ C++ ] STL priority_queue(优先级队列)使用及其底层模拟实现,容器适配器,deque(双端队列)原理了解

STL 中的双端队列到底是啥?

STL中的双端队列

C++ stl queue(单端队列)和stl deque(双端队列)的区别(与循环队列的区别)