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

news2024/11/29 1:53:58

一   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)
    {
        operator++();
        return *this;
    }

    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)
    {
        operator--();
        return *this;
    }

    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;
}

输出:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1052306.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

lv5 嵌入式开发-10 信号机制(下)

目录 1 信号集、信号的阻塞 2 信号集操作函数 2.1 自定义信号集 2.2 清空信号集 2.3 全部置1 2.4 将一个信号添加到集合中 2.5 将一个信号从集合中移除 2.6 判断一个信号是否在集合中 2.7 设定对信号集内的信号的处理方式(阻塞或不阻塞) 2.8 使进程挂起&#xff08;…

NLP 01(介绍)

一、NLP 自然语言处理 (Natural Language rrocessing,简称NLP) 是计算机科学与语言学中关注于计算机与人类语言间转换的领域。 1.1 发展 规则&#xff1a;基于语法 自然语言处理的应用场景: 语音助手 机器翻译 搜索引擎 智能问答

Windows下安装MySQL8详细教程

Windows下安装MySQL8详细教程 因为需要在Windows下安装MySQL8的数据库&#xff0c;做一个临时数据库环境。 1.准备软件 使用社区版本&#xff0c;下载地址如下&#xff1a; https://dev.mysql.com/downloads/mysql/ 使用8.0.16版本&#xff0c;需要在归档中查找 选择版本&a…

pysimpleGui 使用之sg.SaveAs使用

SaveAs与FileBrowse使用一样需要给指定target参数&#xff0c;保存路径 layout [ [ sg.FileBrowse( button_text“请选择单个文件”, # 按钮文本 target“single_path”, # 把选择后的路径保存到key为input_path的对象 # file_types((“All Files”, “.”),), # 默认筛选全部…

Python+Yolov8路面桥梁墙体裂缝识别

程序示例精选 PythonYolov8路面桥梁墙体裂缝识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonYolov8路面桥梁墙体裂缝识别》编写代码&#xff0c;代码整洁&#xff0c;规则&#…

如何应用MBTI职业性格测试来做职业规划

想要有一个不错的职业发展&#xff0c;需要做好职业规划。通常来说&#xff0c;职业规划可以分为三个组成&#xff0c;即定位、目标和路径。应用MBTI职业性格测试&#xff0c;可以对上述三个组成有更清晰的认识&#xff0c;帮助人们完成适合自己的职业规划。 职业性格和职业定…

Pytorch之ResNet图像分类

&#x1f482; 个人主页:风间琉璃&#x1f91f; 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 目录 前言 一、ResNet网络结构 1.residual结构 2.BN(Batch Normalization)层…

反射知识点学习

文章目录 1. Java 反射机制原理示意图1.1 反射相关的主要类1.2 反射的优点和缺点1.3 反射调用优化-关闭访问检查 2. Class 类2.1 基本介绍2.2 Class类的常用方法2.3 获取 Class 类对象 3. 哪些类型有 Class 对象4. 类加载4.1 基本说明4.2 类加载时机4.3 类加载过程图4.4 类加载…

国庆作业1

使用消息队列实现进程之间的通信 代码 write.c #include <myhead.h> //消息结构体 typedef struct {long msgtype; //消息类型char data[1024]; //消息正文 }Msg_ds;#define SIZE sizeof(Msg_ds)-sizeof(long) //正文大小 int main(int argc, cons…

算法-位运算-只出现一次的数字 II

算法-位运算-只出现一次的数字 II 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/bitwise-and-of-numbers-range/description/?envTypestudy-plan-v2&envIdtop-interview-150 1.2 题目描述 2 逐个按位与运算 2.1 思路 最简单的就是直接挨个做与运算&#x…

transformers简介

目录 1、前言 2、网络结构 &#xff08;1&#xff09;、Transformers的总体架构可以分为四部分 &#xff08;2&#xff09;、输入文本包含 &#xff08;3&#xff09;、输出部分包含 &#xff08;4&#xff09;、编码器部分 &#xff08;5&#xff09;、解码器部分 1、前…

dbeaver连接国产数据库

dbeaver是常用的数据库连接工具。但是在连接一些国产的数据库时&#xff0c;因为没有可选的驱动&#xff0c;所以需要我们先设置驱动&#xff0c;在连接。以下是一个连接highgo例子。 首先先新增一个驱动&#xff1a; 在页面的菜单栏&#xff0c;选择 数据库 ->驱动管理器…

SpringBoot整合阿里云OSS文件存储解决方案

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Sp…

【AI视野·今日Sound 声学论文速览 第十五期】Fri, 29 Sep 2023

AI视野今日CS.Sound 声学论文速览 Fri, 29 Sep 2023 Totally 1 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Audio-Visual Speaker Verification via Joint Cross-Attention Authors R. Gnana Praveen, Jahangir Alam使用语音信号进行了说话人验证的…

Web开发-新建Spring Boot项目

目录 Spring Boot 与 Web开发Spring Boot 与 MavenJava 环境搭建下载JDK下载xmapp下载navicat for mysql下载Eclipse配置tomcat配置maven 新建Spring Boot项目 Spring Boot 与 Web开发 Spring Boot 是一种用于简化 Spring 应用程序开发、部署和运行的框架&#xff0c;而 Web 开…

【LeetCode】滑动窗口妙解无重复字符的最长子串

Problem: 3. 无重复字符的最长子串 文章目录 思路算法原理分析暴力枚举 哈希表滑动窗口 复杂度Code 思路 首先我们来分析一下本题的思路 如果读者有看过 长度最小的子数组 的话就可以清楚这个子串其实和子数组是一个道理&#xff0c;都是 连续的一段区间但是呢它们本质上还是存…

【数据结构】队列和栈

大家中秋节快乐&#xff0c;玩了好几天没有学习&#xff0c;今天分享的是栈以及队列的相关知识&#xff0c;以及栈和队列相关的面试题 1.栈 1.1栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作…

Java字符缓冲流自己特有的方法进行读入和写出

代码如下&#xff1a; public class MyWork {public static void main(String[] args) throws IOException{BufferedReader fr new BufferedReader(new FileReader("myfile/abc.txt"));BufferedWriter fw new BufferedWriter(new FileWriter("myfile/test.tx…

(一)gitblit安装教程

(一)gitblit安装教程 (二) gitblit用户使用教程 (三) gitblit管理员手册 目录 前言安装1.下载Java Runtime Requirement 2.设置环境变量3.gitblit内容3.1 gitblit文件夹内容3.2 defaults.properties 主要配置选项 4 配置4.1 准备文件4.2 修改gitblit.properties4.3 修改authori…

第十四届蓝桥杯大赛软件赛决赛 C/C++ 大学 B 组 试题 E: 数三角

[蓝桥杯 2023 国 B] 数三角 【问题描述】 小明在二维坐标系中放置了 n n n 个点&#xff0c;他想在其中选出一个包含三个点的子集&#xff0c;这三个点能组成三角形。然而这样的方案太多了&#xff0c;他决定只选择那些可以组成等腰三角形的方案。请帮他计算出一共有多少种选…