C++ -- 学习系列 std::deque 的原理与使用

一   deque 是什么?

std::deque 是 c++ 一种序列式容器,其与 vector 类似,其底层内存都是连续的,不同的地方在于, vector 是一端开口,在一端放入数据与扩充空间,而 deque 是双端均开口,都可以放入数据与扩充空间。

二  原理

deque中存在两种数组:中控数组与缓存数组,在 deque 初始化时,两种数组均会初始化完成。

1. 缓存数组有多个,缓存数组用于存储真正的元素

2.中控数组用于存放缓存数组的地址,方便快速定位到指定缓存数组,进而快速定位元素的位置

其原理图如下:

其迭代器 Iterator 中需要含有四种信息:

1. 当前元素所在缓存数组的首元素地址

2. 当前元素所在缓存数组的尾元素地址

3. 当前元素在缓存数组中的实际地址

4. 当前元素所在缓存数组在中控数组的地址

三  使用

1. 容器构造

std::deque<T,Allocator>::deque - cppreference.com

函数说明
deque( size_type count,

                const T& value,

                const Allocator& alloc = Allocator() );
定义 count 个值为 value的元素存储到 deque 中             
deque( std::initializer_list<T> init,
       const Allocator& alloc = Allocator() );
通过 list 定义 deque 
#include<iostream>
#include<deque>

template<typename T>
void printDeque(std::deque<T>& tmp_deque)
{
    for(auto iter = tmp_deque.begin(); iter != tmp_deque.end(); iter++)
    {
        std::cout << *iter <<" ";
    }
    std::cout  <<"" << std::endl;
}

int main()
{
    std::deque<int> tmp_deque1(6, 8);
    printDeque(tmp_deque1);

    std::deque<int> tmp_deque2;
    tmp_deque2 = {1, 2 , 3, 4, 5, 6};
    printDeque(tmp_deque2);

    return 0;
}

2. 访问元素

https://en.cppreference.com/w/cpp/container/deque

函数说明
at返回指定下标元素的引用
operator[]同上
front返回容器的首元素引用
back返回容器的尾元素引用
#include<iostream>
#include<deque>


int main()
{
    std::deque<int> tmp_deque2 = {1, 2 , 3, 4, 5, 6};
          // Read Element
    std::cout << tmp_deque2.at(1) << std::endl;
    std::cout << tmp_deque2[2] << std::endl;
    std::cout << tmp_deque2.front() << std::endl;
    std::cout << tmp_deque2.back() << std::endl;

        // Wirte Element
    tmp_deque2.at(1) = 888;
    tmp_deque2[2] = 888;
    tmp_deque2.front() = 888;
    tmp_deque2.back() = 888;

    std::cout << tmp_deque2.at(1) << std::endl;
    std::cout << tmp_deque2[2] << std::endl;
    std::cout << tmp_deque2.front() << std::endl;
    std::cout << tmp_deque2.back() << std::endl;    

    return 0;
}

3. 容器空间

函数说明
empty()返回 deque 是否为空
size()返回 deque 实际存储元素的个数
#include<deque>
#include<iostream>

int  main()
{
    std::deque<int> tmp_deque2;
    tmp_deque2 = {1, 2 , 3, 4, 5, 6};

    std::deque<int> tmp_deque3;
    std::cout << tmp_deque3.empty() << std::endl;
    std::cout << tmp_deque3.size() << std::endl;

    std::cout << tmp_deque2.empty() << std::endl;
    std::cout << tmp_deque2.size() << std::endl;

    return 0;
}

4. 容器修改

std::deque - cppreference.com

函数说明
clear()清空 deque 的内容
push_backappend 元素到 deque 的尾端
pop_back将 deque 的尾端元素弹出
push_frontappend 元素到 deque 的前端
pop_front将 deque 的前端元素弹出
#include<deque>
#include<iostream>

template<typename T>
void printDeque(std::deque<T>& tmp_deque)
{
    for(auto iter = tmp_deque.begin(); iter != tmp_deque.end(); iter++)
    {
        std::cout << *iter <<" ";
    }
    std::cout  <<"" << std::endl;
}

int main(0
{
    std::deque<int> tmp_deque2;
    tmp_deque2 = {1, 2 , 3, 4, 5, 6};
    printDeque(tmp_deque2);

    tmp_deque2.push_back(22);
    tmp_deque2.push_front(33);
    std::cout << tmp_deque2.front() << std::endl;
    std::cout << tmp_deque2.back() << std::endl;

    printDeque(tmp_deque2);

    tmp_deque2.pop_back();
    tmp_deque2.pop_front();
    std::cout << tmp_deque2.front() << std::endl;
    std::cout << tmp_deque2.back() << std::endl;
    printDeque(tmp_deque2);

    tmp_deque2.clear();
    std::cout << tmp_deque2.empty() << std::endl;

    return 0;
}

四  简单实现

   1. 参照原理做了简单的实现,实现了几个简单的函数接口:

  

// my_deque.h

#ifndef MY_DEQUE_H
#define MY_DEQUE_H

#include<vector>
#include<iostream>

// 迭代器, T 为 my_deque 的元素类型, buffserSize 为每个缓存区的大小
template<typename T, int bufferSize>
struct my_deque_iterator
{
    T* M_start; // 当前buffer 的起始位置
    T* M_finish; // 当前buffer 的结尾位置
    T* M_curr; // 当前元素的位置
    T** M_map_node; // 当前 buffer 对应的中控数组的位置

    my_deque_iterator(T* start = nullptr, T* finish = nullptr, T* curr = nullptr, T** map_node = nullptr):
        M_start(start), M_finish(finish), M_curr(curr),M_map_node(map_node)
    {

    }

    my_deque_iterator(const my_deque_iterator& other):M_start(other.M_start), M_finish(other.M_finish), M_curr(other.M_curr),M_map_node(other.M_map_node)
    {

    }

    my_deque_iterator(my_deque_iterator& other):M_start(other.M_start), M_finish(other.M_finish), M_curr(other.M_curr),M_map_node(other.M_map_node)
    {

    }

    my_deque_iterator& operator++()
    {
        if(M_curr == M_finish)
        {
            M_map_node += 1;
            set_M_Node(M_map_node);
        }
        else
        {
            M_curr++;
        }

        return *this;
    }

    my_deque_iterator& operator++(int)
    {
        my_deque_iterator tmp = *this;
        ++*this;
        return tmp;
    }

    my_deque_iterator& operator--()
    {
        if(M_curr == M_start)
        {
            M_map_node -= 1;
            set_M_Node(M_map_node, bufferSize - 1);
        }
        else
        {
            M_curr--;
        }
        return *this;
    }

    my_deque_iterator& operator--(int)
    {
        my_deque_iterator tmp = *this;
        --*this;
        return tmp;
    }

    T&  operator*()
    {
        return *(this->M_curr);
    }

    my_deque_iterator& operator+=(int offset)
    {

        if(offset >=0 && M_curr + offset <= M_finish || offset < 0 && M_curr + offset >= M_start)
        {
            M_curr += offset;
        }
        else
        {
            int diff =  offset >= 0 ? M_finish - M_curr + 1 : M_curr - M_start + 1;

            int offsetNode = offset >= 0 ? (offset - diff) / bufferSize + 1
                                        : ((offset + diff) / bufferSize - 1);

            int offsetBuff = offset >= 0 ? (offset - diff) % bufferSize :
                                           (diff + offset) % bufferSize;

            M_map_node += offsetNode;
            if(offset >= 0)
            {
                set_M_Node(M_map_node, offsetBuff);
            }
            else
            {
                set_M_Node(M_map_node, bufferSize - 1 - offsetBuff);
            }
        }

        return *this;
    }

    my_deque_iterator& operator-=(int offset)
    {
        this->operator+=(-offset);
        return *this;
    }

    my_deque_iterator operator+(int offset)
    {

        my_deque_iterator tmp = *this;
        tmp += offset;
        return tmp;
    }

    my_deque_iterator& operator-(int offset)
    {
        this->operator-=(offset);
        return *this;
    }

    void set_M_Node(T** map_node, int offset = 0)
    {
        M_start = M_map_node[0];
        M_curr = M_start + offset;
        M_finish = M_start + bufferSize - 1;
    }

    bool operator==(my_deque_iterator& other)
    {
        return this->M_curr == other.M_curr;
    }

    bool operator!=(my_deque_iterator& other)
    {
        return this->M_curr != other.M_curr;
    }

    bool operator==(my_deque_iterator&& other)
    {
        return this->M_curr == other.M_curr;
    }

    bool operator!=(my_deque_iterator&& other)
    {
        return this->M_curr != other.M_curr;
    }

    my_deque_iterator& operator=(my_deque_iterator& other)
    {
        this->M_start = other.M_start;
        this->M_curr = other.M_curr;
        this->M_finish = other.M_finish;
        this->M_map_node = other.M_map_node;
        return *this;
    }

    my_deque_iterator& operator=(my_deque_iterator&& other)
    {
        this->M_start = other.M_start;
        this->M_curr = other.M_curr;
        this->M_finish = other.M_finish;
        this->M_map_node = other.M_map_node;
        return *this;
    }


};


template<typename T, int bufferSize>
class my_deque
{
public:
    typedef my_deque_iterator<T, bufferSize> Iterator;

public:

    my_deque(int map_size = 9):M_map_size(map_size){
        M_map = new T*[M_map_size];
        for(int i = 0; i < M_map_size; i++)
        {
            M_map[i] = new T[bufferSize];
            for(int j = 0; j < bufferSize; j++)
            {
                M_map[i][j] = 0;
            }
        }
    }

    ~my_deque()
    {
        for(int i = 0; i < M_map_size; i++)
        {
            delete [] M_map[i];
        }

        delete [] M_map;
    }

    void push_front(T& val){
        // 元素存储到了起始位置
        if(M_start.M_curr == &M_map[0][0])
        {
            reAlloc(M_map_size * 2);
        }
        if(M_start.M_curr == nullptr)
        {
            M_map[M_map_size/2][0] = val;
            M_start = Iterator(&M_map[M_map_size/2][0], &M_map[M_map_size/2][0] + bufferSize - 1, &M_map[M_map_size/2][0], &M_map[M_map_size/2]);
            M_finish = M_start;
        }
        else
        {
            M_start--;
            *M_start.M_curr = val;
        }
    }

    void push_front(T&& val){
          push_front(val);
    }

    void push_back(T& val){
        // 元素存储到了结尾位置
        if(M_finish.M_curr == &M_map[M_map_size-1][bufferSize-1])
        {
            reAlloc(M_map_size * 2);
        }
        if(M_finish.M_curr == nullptr)
        {
            M_map[M_map_size/2][0] = val;
            M_finish = Iterator(&M_map[M_map_size/2][0],
                    &M_map[M_map_size/2][0] + bufferSize - 1, &M_map[M_map_size/2][0], &M_map[M_map_size/2]);
            M_start = M_finish;
        }
        else
        {
            M_finish++;
            *M_finish.M_curr = val;
        }
    }

    void push_back(T&& val){
        push_back(val);
    }

    T pop_front()
    {
        T tmp = *M_start.M_curr;
        M_start++;
        return tmp;
    }

    T pop_back()
    {
        T tmp = *M_finish.M_curr;
        M_finish--;
        return tmp;
    }

    Iterator begin()
    {
        return M_start;
    }

    Iterator end()
    {
        return M_finish + 1;
    }

    int size()
    {
        return bufferSize *(M_finish.M_map_node - M_start.M_map_node - 1) +
                (M_start.M_finish - M_start.M_curr + 1) + (M_finish.M_curr - M_finish.M_start + 1);
    }

    T& front()
    {
        return *M_start.M_curr;
    }

    T& back()
    {
        return *M_finish.M_curr;
    }

    void print()
    {
        for(int i = 0; i < M_map_size; i++)
        {
            for(int j = 0; j < bufferSize; j++)
            {
                std::cout << M_map[i][j] <<" ";
            }
            std::cout << "" << std::endl;
        }
    }

private:

    void reAlloc(int map_size)
    {
        T** tmp = new T*[map_size];

        int ori_mid = M_map_size / 2;
        int new_mid = map_size / 2;
        tmp[new_mid] = M_map[ori_mid];

        // mid to left
        int new_index = new_mid - 1;
        for(int i = ori_mid - 1; i >= 0; i--)
        {
            M_map[new_index--] = tmp[i];
        }

        while (new_index >= 0) {
            M_map[new_index--] = new T[bufferSize];
        }

        // mid to right
        new_index = new_mid + 1;
        for(int i = ori_mid + 1; i < M_map_size; i++)
        {
            M_map[new_index++] = tmp[i];
        }

        while (new_index < map_size) {
            M_map[new_index++] = new T[bufferSize];
        }

        M_map_size = map_size;

        T** tmp1 = M_map;

        M_map = tmp;
        tmp = tmp1;

        delete tmp;
    }

private:
    int  M_map_size; // 中控 数组 的长度
    T**  M_map = nullptr;  // 中控数组
    Iterator   M_start; // 起始元素
    Iterator   M_finish; // 结尾元素
};

#endif // MY_DEQUE_H



// main.cpp
#include<iostream>
#include<my_deque.h>

void testMyDeque()
{
   my_deque<int, 3> tmp_deque;
    tmp_deque.push_back(1);
//    std::cout << " --------- " << std::endl;
//    tmp_deque.print();
//    std::cout << " --------- " << std::endl;

    tmp_deque.push_back(2);

//    tmp_deque.print();
//    std::cout << " --------- " << std::endl;

    tmp_deque.push_back(3);
//    tmp_deque.print();
//    std::cout << " --------- " << std::endl;

    tmp_deque.push_back(4);
//    tmp_deque.print();
//    std::cout << " --------- " << std::endl;

    tmp_deque.push_back(5);
//    tmp_deque.print();
//    std::cout << " --------- " << std::endl;

    tmp_deque.push_front(2);
//    tmp_deque.print();
//    std::cout << " --------- " << std::endl;

    tmp_deque.push_front(3);
//    tmp_deque.print();
//    std::cout << " --------- " << std::endl;

    tmp_deque.push_front(4);
//    tmp_deque.print();
//    std::cout << " --------- " << std::endl;

    tmp_deque.push_front(5);
//    tmp_deque.print();

    for(my_deque<int, 3>::Iterator iter = tmp_deque.begin(); iter != tmp_deque.end(); iter++)
    {
        std::cout << *iter << " ";
    }
    std::cout << " --------- " << std::endl;

    auto iter = tmp_deque.begin();
    std::cout << *iter <<std::endl;
    //std::cout << *(iter-3) <<std::endl;
//    std::cout << (iter).M_start <<std::endl;
//    std::cout << (iter).M_curr <<std::endl;
//    std::cout << (iter).M_finish <<std::endl;
//    std::cout << (iter).M_map_node <<std::endl;

    std::cout << " --------- " << std::endl;

    std::cout << *(iter+3) <<std::endl;
//    std::cout << (iter+3).M_start <<std::endl;
//    std::cout << (iter+3).M_curr <<std::endl;
//    std::cout << (iter+3).M_finish <<std::endl;
//    std::cout << (iter+3).M_map_node <<std::endl;

    std::cout << " --------- " << std::endl;

    auto iter2 = iter + 3;
    std::cout << *(iter2-3) <<std::endl;
//    std::cout << (iter2-3).M_start <<std::endl;
//    std::cout << (iter2-3).M_curr <<std::endl;
//    std::cout << (iter2-3).M_finish <<std::endl;
//    std::cout << (iter2-3).M_map_node <<std::endl;

    std::cout << " --------- " << std::endl;

    std::cout << *(iter+5) <<std::endl;
//    std::cout << (iter+5).M_start <<std::endl;
//    std::cout << (iter+5).M_curr <<std::endl;
//    std::cout << (iter+5).M_finish <<std::endl;
//    std::cout << (iter+5).M_map_node <<std::endl;

    std::cout << "size: " << tmp_deque.size() << std::endl;
    std::cout << "pop_back: " << tmp_deque.pop_back() << std::endl;
    std::cout << "size: " << tmp_deque.size() << std::endl;
    std::cout << "pop_front: " << tmp_deque.pop_front() << std::endl;
    std::cout << "size: " << tmp_deque.size() << std::endl;
    std::cout << "empty: " << tmp_deque.empty() << std::endl;
}

int main()
{
    testMyDeque();

    return 0;
}

输出:

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值