1. 程式人生 > >從零開始寫STL-容器-雙端隊列

從零開始寫STL-容器-雙端隊列

這一 偏移 nis log index end ref 分配 locate

從零開始寫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-容器-雙端隊列