首页 > 编程语言 >C++ STL第三篇(搞清楚deque原理和有多少用法)

C++ STL第三篇(搞清楚deque原理和有多少用法)

时间:2024-03-16 23:34:42浏览次数:37  
标签:deque end r1 r2 容器 STL elem C++

deque

Vector容器是单向开口的连续内存空间,deque则是一种双向开口的连续线性空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector容器也可以在头尾两端插入元素,但是在其头部操作效率奇差,无法被接受。

image-20240312101135297

Deque容器和vector容器最大的差异,一在于deque允许使用常数项时间对头端进行元素的插入和删除操作。二在于deque没有容量的概念,因为它是动态的以分段连续空间组合而成,随时可以增加一段新的空间并链接起来,换句话说,像vector那样,”旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在deque身上是不会发生的。也因此,deque没有必须要提供所谓的空间保留(reserve)功能.

虽然deque容器也提供了Random Access Iterator,但是它的迭代器并不是普通的指针,其复杂度和vector不是一个量级,这当然影响各个运算的层面。因此,除非有必要,我们应该尽可能的使用vector,而不是deque。对deque进行的排序操作,为了最高效率,可将deque先完整的复制到一个vector中,对vector容器进行排序,再复制回deque.

实现原理

Deque容器是连续的空间,至少逻辑上看来如此,连续现行空间总是令我们联想到array和vector,array无法成长,vector虽可成长,却只能向尾端成长,而且其成长其实是一个假象,事实上(1) 申请更大空间 (2)原数据复制新空间 (3)释放原空间 三步骤,如果不是vector每次配置新的空间时都留有余裕,其成长假象所带来的代价是非常昂贵的。

Deque是由一段一段的定量的连续空间构成。一旦有必要在deque前端或者尾端增加新的空间,便配置一段连续定量的空间,串接在deque的头端或者尾端。Deque最大的工作就是维护这些分段连续的内存空间的整体性的假象,并提供随机存取的接口,避开了重新配置空间,复制,释放的轮回,代价就是复杂的迭代器架构。

既然deque是分段连续内存空间,那么就必须有中央控制,维持整体连续的假象,数据结构的设计及迭代器的前进后退操作颇为繁琐。Deque代码的实现远比vector或list都多得多。

Deque采取一块所谓的map(注意,不是STL的map容器)作为主控,这里所谓的map是一小块连续的内存空间,其中每一个元素(此处成为一个结点)都是一个指针,指向另一段连续性内存空间,称作缓冲区。缓冲区才是deque的存储空间的主体。

image-20240312105517966

声明方法

  1. 默认构造形式:使用默认构造函数创建一个空的deque容器。
deque<T> deqT;
deque<int> r1;//默认构造形式:创建一个空的deque容器
  1. 范围构造函数:使用指定范围内的元素创建deque容器,将[beg, end)区间中的元素拷贝给本身。
deque<T> deqT(beg, end);
int arr[] = { 1,2,3,4,5 };
deque<int> r2(arr, arr + 5);// 范围构造函数:将指定范围内的元素拷贝给本身

其中,beg是指向范围起始位置的迭代器,end是指向范围结束位置的迭代器。

  1. 值构造函数:使用指定值创建deque容器,将n个elem拷贝给本身。
deque<T> deqT(n, elem);
deque<int> r3(3, 100);// 创建包含3个值为10的元素的deque

其中,n是要创建的元素数量,elem是要拷贝的元素值。

  1. 拷贝构造函数:使用另一个deque容器进行拷贝构造,创建一个新的deque容器。
deque<T> deqT(deq);
deque<int> deq4(deq2);

其中,deq是要进行拷贝的deque容器。

赋值操作

  1. assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。

  2. assign(n, elem);//将n个elem拷贝赋值给本身。

  3. deque&operator=(const deque &deq); //重载等号操作符

  4. swap(deq);// 将deq与本身的元素互换

template <typename T>
void print(const T& r1, const T& r2){
	std::cout << "r1: ";
	for (const auto& elem : r1) {
		std::cout << elem << " ";
	}
	std::cout << std::endl;

	std::cout << "r2: ";
	for (const auto& elem : r2) {
		std::cout << elem << " ";
	}
	std::cout << std::endl;
	cout << "-------------" << endl;
}
deque<int> r1, r2;
deque<int> data = { 1,2,3,4,5 };
r1.assign(data.begin(), data.end());
print(r1, r2);
r2.assign(3, 100);
print(r1, r2);
r2 = r1;
print(r1, r2);
r1.swap(r2);
print(r1, r2);

image-20240316230550602

容量大小

deque.size();//返回容器中元素的个数

deque.empty();//判断容器是否为空

deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除。

	deque<int> r1, r2;
	deque<int> data = { 1,2,3,4,5 };
	r1.assign(data.begin(), data.end());
	cout << "r2.empty:" << r2.empty() << endl;
	print(r1, r2);
	r2.assign(3, 100);
	print(r1, r2);
	cout << "r1 size: " << r1.size() << endl;
	r1.resize(8);
	r2.resize(8, 100);
	print(r1, r2);

image-20240316231017869

插入和删除

push_back(elem);//在容器尾部添加一个数据

push_front(elem);//在容器头部插入一个数据

pop_back();//删除容器最后一个数据

pop_front();//删除容器第一个数据

at(idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range。

operator[];//返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错。

front();//返回第一个数据。

back();//返回最后一个数据

insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置。

insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。

insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。

clear();//移除容器的所有数据

erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。

erase(pos);//删除pos位置的数据,返回下一个数据的位置。insert 函数的参数 pos 实际上是一个迭代器(iterator)类型,而不是简单的整数类型
deque<int> r1;
r1.push_back(27);
print(r1);
r1.push_front(12);
print(r1);
// 返回索引idx所指的数据,如果idx越界,抛出out_of_range。
try {
	int v = r1.at(12);
	cout << "Value at index 12: " << v << std::endl;
	v = r1.at(127);
}
catch (const out_of_range& e) {
	cerr << "Out of range error: " << e.what() << std::endl;
}
// 返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错。
int value = r1[1]; // 可能会直接出错,因为不会抛出异常

cout << "First value: " << r1.front() << std::endl;// 返回第一个数据
cout << "Last value: " << r1.back() << std::endl;// 返回最后一个数据

//在pos位置插入一个elem元素的拷贝,返回新数据的位置。
auto it = r1.insert(r1.begin() + 2, 99);
/*在代码中使用 auto 关键字的作用是让编译器根据等号右边表达式的类型推导出左边变量的类型,从而简化代码书写。
r1.insert(r1.begin() + 2, 99) 这个表达式的返回类型是一个迭代器(iterator),它指向插入的元素位置。
由于迭代器的类型可能很复杂且并非显式指定,因此可以使用 auto 让编译器自动推导出正确的类型。*/
// 在pos位置插入n个elem数据
r1.insert(r1.begin() + 2, 3, 88);
print(r1);
// 在pos位置插入[beg,end)区间的数据
deque<int> r2 = { 1,2,3 };
r1.insert(r1.begin() + 2, r2.begin(), r2.end());
r2 = r1;
print(r1);
r1.clear();
r1 = r2;
r1.erase(r1.begin() + 2, r1.begin() + 5);
print(r1);
r1.erase(r1.begin()+2);

image-20240316232442997

标签:deque,end,r1,r2,容器,STL,elem,C++
From: https://www.cnblogs.com/ivanlee717/p/18077885

相关文章

  • (C++)DP动态规划
    天下苦DP久已。​ DP非常重要,2022年蓝桥杯应该改名为DP杯,至于2023年那个我觉得应该叫做暴力杯。​ DP的核心思想在于,通过前面几个状态来推导下一个数据是什么,也就是走一步是一步。其本质实际上是记忆化搜索,但是有些玄学的事情就是,有时候记忆化会因为玄学递归问题TLE,但DP的那四......
  • c++机试一些提示
    1、优先队列的使用,头文件#include;优先队列定义:priority_queue<int,vector<int>,greater<int>>pq;(整数数据类型,小顶堆)。例题:哈夫曼树#include<iostream>#include<queue>usingnamespacestd;intmain(){intn;cin>>n;priority_queue<i......
  • 华为OD机试 C++ -文件缓存系统
    文件缓存系统前言:本专栏将持续更新互联网大厂机试真题,并进行详细的分析与解答,包含完整的代码实现,希望可以帮助到正在努力的你。关于大厂机试流程、面经、面试指导等,如有任何疑问,欢迎联系我,wechat:steven_moda;email:[email protected];备注:CSDN。题目描述请设计一个文件缓......
  • C++ 枚举
    C++枚举5.4.1普通枚举枚举的定义:,枚举类型是通过enum关键字定义的,比如定义颜色类型enumColor{RED,//默认值为0GREEN,//默认值为1BLUE//默认值为2};ColormyColor=RED;注意:(1)括号内为以逗号分隔,大括号结尾要有分号(2)枚举类型就相当于......
  • STL——Vector的使用
    目录一.Vector的简介二.Vector的使用1.创建vector2.vector的初始化2.1.花括号直接初始化2.2.小括号初始化2.2.1一个参数的情况2.2.2.两个参数的情况2.3.用另一个vector初始化三.vector的元素访问1.at2.operator[]3.迭代器访问四.vector的常用函数1.empty()2.siz......
  • 10. C++关键字virtual用法
    1.C++关键字:virtual用法1.1概念virtual是C++OO机制中很重要的一个关键字。主要用在两个方面:虚函数、纯虚函数和虚基类、虚继承。1.2虚函数virtual放在函数的返回值前面,用于表示该类成员函数为虚函数;父类虚函数前的virtual必须写;子类虚函数前的virtual可以省略,因为不......
  • 使用c++容器string相关完成
    //把邮箱地址字符串[email protected],取出其中用户名字符串打印stringgetUsername(string&s){   intpops=s.find('@');   stringusername=s.substr(0,pops);   returnusername;}//大小写转换 使用标准库提供俩函数,单个字符为操作对象stringstr=......
  • 详解MySQL的MVCC(ReadView部分解析C++源码)
    文章目录1.什么是MVCC2.MVCC核心组成(三大件)2.1MVCC为什么需要三大件3.隐藏字段4.undolog4.1模拟版本链数据形成过程5.ReadView5.1m_ids5.2m_creator_trx_id5.3m_low_limit_id5.4m_up_limit_id5.5可见性分析算法6.MVCC流程模拟6.1RC隔离级别6.2RR隔离......
  • C++学习笔记——002
    在一个类里建立一个const时,不能给他初值:classfoo{public:foo():i(100){}private:constinti=100;//错误!!!};//可以通过这样的方式来进行初始化foo::foo():i(100){} classTest{public:Test():a(0){}enum{size1=100,size2=200};......
  • C++类模板与友元详解
    C++模板下面分四种情况分别讨论。1.函数、类、类的成员函数作为类模板的友元函数、类、类的成员函数都可以作为类模板的友元。程序示例如下:void Func1() {  }class A {  };class B{public:    void Func() { }};template <class T>class Tmpl{......