vector, list, deque

本文详细介绍了C++容器中的list、vector与deque三种数据结构的特点与区别,包括它们的构造函数、增加和删除元素、遍历、大小判断等常用函数。着重探讨了它们在内存分配、元素访问效率、插入删除操作等方面的性能差异,并通过实例展示了deque与vector内存分配的不同。此外,还对比了deque与vector、list的主要特点与适用场景。

list容器介绍

相对于vector容器的连续线性空间,list是一个双向链表,它有一个重要性质:插入操作和删除操作都不会造成原有的list迭代器失效,每次插入或删除一个元素就配置或释放一个元素空间。也就是说,对于任何位置的元素插入或删除,list永远是常数时间。

常用函数

(1)    构造函数

list<Elem> c:创建一个空的list

list<Elem> c1(c2):复制另一个同类型元素的list

list<Elem>c(n):创建n个元素的list,每个元素值由默认构造函数确定

list<Elem>c(n,elem):创建n个元素的list,每个元素的值为elem

list<Elem>c(begin,end):由迭代器创建list,迭代区间为[begin,end)

(2)    大小、判断函数

Int size() const:返回容器元素个数

bool empty() const:判断容器是否为空,若为空则返回true

(3)    增加、删除函数

void push_back(const T& x):list元素尾部增加一个元素x

void push_front(const T& x):list元素首元素钱添加一个元素X

void pop_back():删除容器尾元素,当且仅当容器不为空

void pop_front():删除容器首元素,当且仅当容器不为空

void remove(const T& x):删除容器中所有元素值等于x的元素

void clear():删除容器中的所有元素

iterator insert(iterator it, const T& x ):在迭代器指针it前插入元素x,返回x迭代器指针

void insert(iterator it,size_type n,const T& x):迭代器指针it前插入n个相同元素x

void insert(iterator it,const_iterator first,const_iteratorlast):把[first,last)间的元素插入迭代器指针it前

iterator erase(iterator it):删除迭代器指针it对应的元素

iterator erase(iterator first,iterator last):删除迭代器指针[first,last)间的元素

(4)    遍历函数

iterator begin():返回首元素的迭代器指针

iterator end():返回尾元素之后位置的迭代器指针

reverse_iterator rbegin():返回尾元素的逆向迭代器指针,用于逆向遍历容器

reverse_iterator rend():返回首元素前一个位置的迭代器指针

reference front():返回首元素的引用

reference back():返回尾元素的引用 

(5)    操作函数

void sort():容器内所有元素排序,默认是升序

template<class Pred>void sort(Pred pr):容器内所有元素根据预断定函数pr排序

void swap(list& str):两list容器交换功能

void unique():容器内相邻元素若有重复的,则仅保留一个

void splice(iterator it,list& li):队列合并函数,队列li所有函数插入迭代指针it前,x变成空队列

void splice(iterator it,list& li,iterator first):队列li中移走[first,end)间元素插入迭代指针it前

void splice(iterator it,list& li,iterator first,iterator last):x中移走[first,last)间元素插入迭代器指针it前

void reverse():反转容器中元素顺序



vector容器介绍

vector是STL中最常见的容器,它是一种顺序容器,支持随机访问。 vector是一块连续分配的内存,从数据安排的角度来讲,和数组极其相似, 不同的地方就是:数组是静态分配空间,一旦分配了空间的大小,就不可再改变了; 而vector是动态分配空间,随着元素的不断插入,它会按照自身的一套机制不断扩充自身的容量。

vector的扩充机制:按照容器现在容量的一倍进行增长。 vector容器分配的是一块连续的内存空间,每次容器的增长,并不是在原有连续的内存空间后再进行简单的叠加, 而是重新申请一块更大的新内存,并把现有容器中的元素逐个复制过去,然后销毁旧的内存。 这时原有指向旧内存空间的迭代器已经失效,所以当操作容器时,迭代器要及时更新

vector类常用的函数如下所示:

    1.构造函数

  • vector():创建一个空vector
  • vector(int nSize):创建一个vector,元素个数为nSize
  • vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
  • vector(const vector&):复制构造函数
  • vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中

    2.增加函数

  • void push_back(const T& x):向量尾部增加一个元素X
  • iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
  • iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
  • iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据

   3.删除函数

  • iterator erase(iterator it):删除向量中迭代器指向元素
  • iterator erase(iterator first,iterator last):删除向量中[first,last)中元素
  • void pop_back():删除向量中最后一个元素
  • void clear():清空向量中所有元素

  4.遍历函数

  • reference at(int pos):返回pos位置元素的引用
  • reference front():返回首元素的引用
  • reference back():返回尾元素的引用
  • iterator begin():返回向量头指针,指向第一个元素
  • iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置
  • reverse_iterator rbegin():反向迭代器,指向最后一个元素
  • reverse_iterator rend():反向迭代器,指向第一个元素之前的位置

  5.判断函数

  • bool empty() const:判断向量是否为空,若为空,则向量中无元素

  6.大小函数

  • int size() const:返回向量中元素的个数
  • int capacity() const:返回当前向量张红所能容纳的最大元素值
  • int max_size() const:返回最大可允许的vector元素数量值

  7.其他函数

  • int reserve() const:给向量重新分配20个空间
  • void swap(vector&):交换两个同类型向量的数据
  • void assign(int n,const T& x):设置n个值为x的元素,并且会清除之前元素
  • void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素

进一步理解vector,如下图所示:


    当执行代码vector<int> v(2,5)时,在内存里建立了2个整形元素空间,值是5.当增加一个元素时,原有的空间由2个编程4个整形元素空间,并把元素1放入第3个整形空间,第4个空间作为预留空间。当增加元素2时,直接把值2放入第4个空间。当增加元素3时,由于原有向量中没有预留空间,则内存空间由4个变为8个整形空间,并把值放入第5个内存空间。
   总之,扩大新元素时,如果超过当前的容量,则容量会自动扩充2倍,如果2倍容量仍不足,则继续扩大2倍。本图是直接在原空间基础上画的新增空间,其实要复杂的多,包括重新配置、元素移动、释放原始空间的过程。因此对vector容器而言,当增加新的元素时,有可能很快完成(直接存在预留空间中),有可能稍慢(扩容后再放新元素);对修改元素值而言是较快的;对删除元素来说,弱删除尾部元素较快,非尾部元素稍慢,因为牵涉到删除后的元素移动。

deque容器介绍


deque是双向开口的连续性存储空间。虽说是连续性存储空间,但这种连续性只是表面上的,实际上它的内存是动态分配的,它在堆上分配了一块一块的动态储存区,每一块动态存储去本身是连续的,deque自身的机制把这一块一块的存储区虚拟地连在一起。

        它首次插入一个元素,默认会动态分配512字节空间,当这512字节空间用完后,它会再动态分配自己另外的512字节空间,然后虚拟地连在一起。deque的这种设计使得它具有比vector复杂得多的架构、算法和迭代器设计。它的性能损失比之vector,是几个数量级的差别。所以说,deque要慎用


(1)    构造函数

deque():创建一个空deque

deque(int nSize):创建一个deque,元素个数为nSize

deque(int nSize,const T& t):创建一个deque,元素个数为nSize,且值均为t

deque(const deque &):复制构造函数

(2)    增加函数

void push_front(const T& x):双端队列头部增加一个元素X

void push_back(const T& x):双端队列尾部增加一个元素x

iterator insert(iterator it,const T& x):双端队列中某一元素前增加一个元素x

void insert(iterator it,int n,const T& x):双端队列中某一元素前增加n个相同的元素x

void insert(iterator it,const_iterator first,const_iteratorlast):双端队列中某一元素前插入另一个相同类型向量的[forst,last)间的数据

(3)    删除函数

Iterator erase(iterator it):删除双端队列中的某一个元素

Iterator erase(iterator first,iterator last):删除双端队列中[first,last)中的元素

void pop_front():删除双端队列中最前一个元素

void pop_back():删除双端队列中最后一个元素

void clear():清空双端队列中最后一个元素

(4)    遍历函数

reference at(int pos):返回pos位置元素的引用

reference front():返回手元素的引用

reference back():返回尾元素的引用

iterator begin():返回向量头指针,指向第一个元素

iterator end():返回指向向量中最后一个元素下一个元素的指针(不包含在向量中)

reverse_iterator rbegin():反向迭代器,指向最后一个元素

reverse_iterator rend():反向迭代器,指向第一个元素的前一个元素

(5)    判断函数

bool empty() const:向量是否为空,若true,则向量中无元素

(6)    大小函数

Int size() const:返回向量中元素的个数

int max_size() const:返回最大可允许的双端对了元素数量值

(7)    其他函数

void swap(deque&):交换两个同类型向量的数据

void assign(int n,const T& x):向量中第n个元素的值设置为x


deque与vector内存分配比较:

  1. // deque.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include<iostream>  
  6. #include<deque>  
  7. #include<vector>  
  8.   
  9. using namespace std;  
  10. int _tmain(int argc, _TCHAR* argv[])  
  11. {  
  12.     vector<int>v(2);  
  13.     v[0]=10;  
  14.     int *p = &v[0];  
  15.     cout<<"vector第一个元素迭代指针*p="<<*p<<endl;  
  16.     v.push_back(20);  
  17.     cout<<"vector容量变化后原vector第1个元素迭代指针*p="<<*p<<endl;  
  18.   
  19.     deque<int> d(2);  
  20.     d[0]=10;  
  21.     int *q = &d[0];  
  22.     cout<<"deque第一个元素迭代指针*q="<<*q<<endl;  
  23.     d.push_back(20);  
  24.     cout<<"deque容量变化后第一个元素迭代器指针*q="<<*q<<endl;  
  25. }  

程序运行结果如下图所示

该段程序的功能是:deque、vector初始化后大小为2,第一个元素都为10,当通过push_back函数分别给两个容器增加一个元素后,从结果发现原先保持的指针元素值对vector容器前后发生了变化,而对deque容器前后没有发生变化。原因为,在建立vector容器时,一般来说伴随这建立空间->填充数据->重建更大空间->复制原空间数据->删除原空间->添加新数据,如此反复,保证vector始终是一块独立的连续内存空间;在建立deque容器时,一般便随着建立空间->建立数据->建立新空间->填充新数据,如此反复,没有原空间数据的复制和删除过程,是由多个连续的内存空间组成的。


与vector相比,deque功能上的不同之处在于:

1)两端都能快速插入元素和删除元素(vector只在尾端快速进行此类操作)。

2)存取元素时,deque的内部结构会多一个间接过程,所以元素的存取和迭代器的动作会稍稍慢一些。

3)迭代器需要在不同区块间跳转,所以必须是特殊的智能型指针,非一般指针。

4)在对内存区块有所限制的系统中(例如PC系统),deque可以内含更多元素,因为它使用不止一块内存。因此deque的max_size()可能更大。

5)deque不支持对容量和内存重分配时机的控制。特别要注意的是,除了头尾两端,在任何地方插入或删除元素,都将导致指向deque元素的任何指针、引用、迭代器失效。不过,deque的内存重分配优于vector,因为其内部结构显示,deque不必在内存重分配时复制所有元素。

6)deque的内存区块不再被使用时,会被释放。deque的内存大小是可缩减的。



deque的保存形式如下:

[堆1]
...
[堆2]
...
[堆3]

每个堆保存好几个元素,然后堆和堆之间有指针指向,看起来像是list和vector的结合品,不过确实也是如此
deque可以让你在前面快速地添加删除元素,或是在后面快速地添加删除元素,然后还可以有比较高的随机访问速度

vector是可以快速地在最后添加删除元素,并可以快速地访问任意元素
list是可以快速地在所有地方添加删除元素,但是只能快速地访问最开始与最后的元素
deque在开始和最后添加元素都一样快,并提供了随机访问方法,像vector一样使用[]访问任意元素,但是随机访问速度比不上vector快,因为它要内部处理堆跳转
deque也有保留空间.另外,由于deque不要求连续空间,所以可以保存的元素比vector更大,这点也要注意一下.还有就是在前面和后面添加元素时都不需要移动其它块的元素,所以性能也很高。

 

以下情形,最好采用deque:

1)需要在两端插入和删除元素。

2)无需引用容器内的元素。

3)要求容器释放不再使用的元素。

 

deque的各项操作只在以下几点和vector不同:

1)deque不提供容量操作(capacity()和reserve())。

2)deque直接提供函数,用以完成头部元素的插入和删除(push_front()和pop_front())。

 

除了at(),没有任何成员函数会检查索引或迭代器是否有效。元素的插入或删除可能导致内存重新分配,所以任何插入或删除动作都会使所有指向deque元素的指针、引用和迭代器失效。惟一例外的是在头部或尾部插入元素,操作之后,指针和引用仍然有效,但迭代器将失效。



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值