先来回顾一下,STL的组成
vector :
deque:(double -ended queue)顺序容器,双端队列,双向开口。
实际上是这样的,数据的存储是分段的,翻看C++的文档,说的是 deque 的元素不是相接存储
不管是从头插入,还是从尾插入 都很快
如果往中间插入元素,那么涉及到移动其他元素,效率会比较低
std::deque
(double-ended queue,双端队列)是有下标顺序容器,它允许在它的首尾两端快速插入及删除。另外,在 deque 任一端插入或删除不会使指向其余元素的指针或引用失效。
与 std::vector 相反,deque 的元素不是相接存储的:典型实现用单独分配的固定尺寸数组的序列,外加额外的登记,这表示下标访问必须进行二次指针解引用,与之相比 vector 的下标访问只进行一次。
#include <queue>
在标头 | ||
template< class T, | (1) | |
namespace pmr { template< class T > | (2) | (C++17 起) |
std::deque
(double-ended queue,双端队列)是有下标顺序容器,它允许在它的首尾两端快速插入及删除。另外,在 deque 任一端插入或删除不会使指向其余元素的指针或引用失效。
与 std::vector 相反,deque 的元素不是相接存储的:典型实现用单独分配的固定尺寸数组的序列,外加额外的登记,这表示下标访问必须进行二次指针解引用,与之相比 vector 的下标访问只进行一次。
deque 的存储按需自动扩展及收缩。扩张 deque 比扩张 std::vector 更优,因为它不涉及到复制既存元素到新内存位置。另一方面,deque 典型地拥有较大的最小内存开销;只保有一个元素的 deque 必须分配它的整个内部数组(例如 64 位 libstdc++ 上是对象尺寸的 8 倍;64 位 libc++ 上是对象尺寸的 16 倍和 4096 字节中的较大者)。
deque 上常见操作的复杂度(效率)如下:
- 随机访问——常数 O(1)
- 在结尾或起始插入或移除元素——常数 O(1)
- 插入或移除元素——线性 O(n)
std::deque
满足容器 (Container) 、知分配器容器 (AllocatorAwareContainer) 、序列容器 (SequenceContainer) 和可逆容器 (ReversibleContainer) 的要求。
例子:证明deque 比 vector 效率要好的多,不用和vector一样,频繁的移动所有数据,进而copy 构造函数,析构函数。
class Teacher6 {
public:
Teacher6(int age):mage(age) {
cout << "Teacher6 gouzao mage = " << mage << " this = "<< this << endl;
}
~Teacher6() {
cout << "Teacher6 xigou" << " this = " << this << endl;
}
Teacher6(const Teacher6 &obj) {
this->mage = obj.mage;
cout << "Teacher6 copy gouzao" << " this = " << this <<" obj = " << &obj << endl;
}
Teacher6 & operator=(Teacher6 &obj) {
this->mage = obj.mage;
cout << "Teacher6 operator= " << " this = " << this << " obj = " << &obj << endl;
return *this;
}
int mage;
};
//测试一下,deque是否和vector一样,每次加新的内存,都要 调用copy 构造方法
void main() {
deque<Teacher6> deq;
for (size_t i = 0; i < 5; i++)
{
deq.push_back(Teacher6(i));
}
for (size_t i = 0; i < 5; i++)
{
deq.push_front(Teacher6(i));
}
}
从结果来看:确实是这样。
Teacher6 gouzao mage = 0 this = 00FCFA9C
Teacher6 copy gouzao this = 015A6E68 obj = 00FCFA9C
Teacher6 xigou this = 00FCFA9C
Teacher6 gouzao mage = 1 this = 00FCFA9C
Teacher6 copy gouzao this = 015A6E6C obj = 00FCFA9C
Teacher6 xigou this = 00FCFA9C
Teacher6 gouzao mage = 2 this = 00FCFA9C
Teacher6 copy gouzao this = 015A6E70 obj = 00FCFA9C
Teacher6 xigou this = 00FCFA9C
Teacher6 gouzao mage = 3 this = 00FCFA9C
Teacher6 copy gouzao this = 015A6E74 obj = 00FCFA9C
Teacher6 xigou this = 00FCFA9C
Teacher6 gouzao mage = 4 this = 00FCFA9C
Teacher6 copy gouzao this = 015A5F78 obj = 00FCFA9C
Teacher6 xigou this = 00FCFA9C
Teacher6 gouzao mage = 0 this = 00FCFA90
Teacher6 copy gouzao this = 015A5FC4 obj = 00FCFA90
Teacher6 xigou this = 00FCFA90
Teacher6 gouzao mage = 1 this = 00FCFA90
Teacher6 copy gouzao this = 015A5FC0 obj = 00FCFA90
Teacher6 xigou this = 00FCFA90
Teacher6 gouzao mage = 2 this = 00FCFA90
Teacher6 copy gouzao this = 015A5FBC obj = 00FCFA90
Teacher6 xigou this = 00FCFA90
Teacher6 gouzao mage = 3 this = 00FCFA90
Teacher6 copy gouzao this = 015A5FB8 obj = 00FCFA90
Teacher6 xigou this = 00FCFA90
Teacher6 gouzao mage = 4 this = 00FCFA90
Teacher6 copy gouzao this = 015AFF24 obj = 00FCFA90
Teacher6 xigou this = 00FCFA90
Teacher6 xigou this = 015A5F78
Teacher6 xigou this = 015A6E74
Teacher6 xigou this = 015A6E70
Teacher6 xigou this = 015A6E6C
Teacher6 xigou this = 015A6E68
Teacher6 xigou this = 015A5FC4
Teacher6 xigou this = 015A5FC0
Teacher6 xigou this = 015A5FBC
Teacher6 xigou this = 015A5FB8
Teacher6 xigou this = 015AFF24
例子,访问遍历的方式除了 []外,还可以使用at()
以及测试iterator,const_iterator, reverse_iterator
void main() {
deque<Teacher6> deq;
for (size_t i = 0; i < 5; i++)
{
deq.push_back(Teacher6(i));
}
for (size_t i = 0; i < 5; i++)
{
deq.push_front(Teacher6((i+1)*8));
}
cout << deq.size() << endl;
deque<Teacher6>::iterator it = deq.begin();
//使用 iterator 遍历
for (; it != deq.end(); ++it)
{
Teacher6 tea = *it;
cout << tea.mage << endl;
}
//cbegin 这个c 是const 的意思,意思是拿到的迭代器,不能通过迭代器改动值
deque<Teacher6>::const_iterator it1 = deq.cbegin();
for (; it1 != deq.cend(); ++it1)
{
//(*it1).mage = 98;//build error.
Teacher6 tea = *it1;
cout << tea.mage << endl;
}
// rbegin()函数的返回值的 reverse_iterator,表示将最后一个作为第一个,最后一个开始的iterator,注意,到最后一个也是++it
deque<Teacher6>::reverse_iterator it2 = deq.rbegin();
for (; it2 != deq.rend(); ++it2)
{
Teacher6 tea = *it2;
cout << tea.mage << endl;
}
//访问第三个 teacher,将其mage改成999
deq.at(2).mage = 999;
cout << deq.at(2).mage << endl;
deq[3].mage = 888;
cout << deq[3].mage << endl;
cout << "duandian" << endl;
}
stack:栈,先进后出的结构
和vector 的区别:支持从中间删除数据,添加数据
stack,只支持从栈顶放入元素,以及从栈顶取出元素
queue:队列,普通队列。先进先出
list 是一个双向链表。
不需要各个元素之间的内存连在一起。
查找效率不高
插入数据和删除数据效率高。
list 和 vector的区别:
a.vector类似于数组,内存是连续的; list 是双向链表,内存空间并不连续。
b.vector 从中间或者开头插入元素效率比较低,但是list插入元素效率比较高。
C. vector查找元素快,这是因为内存是连续的,通过下标查找,知道首地址,知道每个vector存的啥,就能很快的跳一段距离,找到想要的元素。
但是list不行,因为是双向链表,一个链接一个,知道首元素,查找next,然后查找next的next,直到找到为止,这就比较慢了
d.vector当内存不够时,会重新找一块内存,将原来的内存对象都copy构造复制到新的内存,对原来内存对象析构,效率很低,要谨慎使用。
forward_list 单向链表 c++11新增
比list少一个指向,只能往头部插入元素
set
set/map 内部实现的数据结构 多为红黑树
我们往这种容器中保存数据时候,不需要我们指定数据位置。
这种容器会根据内部的算法自动安排一个位置
set中的元素值不能重复,如果想重复,请使用mulitset
插入的时候,因为set/map需要找一个合适的位置(根据内部算法),因此比较慢。
查找的时候,会很快。
map
key/value ,一般都是通过key找value。
通过key 找value 特别快。但是存储的时候,由于内部要计算把你放在哪里,因此比较慢
不允许key 一样。
//测试map
template<typename It>
void print_insertion_status(It it, bool success)
{
std::cout << "插入 " << it->first << (success ? " 成功\n" : " 失败\n");
}
void main() {
map<int, string> ma;
pair<map<int, string>::iterator, bool> su;
//map.insert的返回值 是一个pair,这个pair第一个元素是 map的iterator,第二个元素是 bool,代表是否成功
su = ma.insert(make_pair<int,string>(1,"nihao"));
cout << su.second << endl;
if (su.second) {
map<int, string>::iterator it = su.first;
cout << " (*it).first = " << (*it).first << endl;
cout << " (*it).second = " << (*it).second.c_str() << endl;
}
su = ma.insert({ 2, "jiushi2" });
cout << su.second << endl; // 1
su = ma.insert({ 2, "sdf " });
cout << su.second << endl;//失败了 就是0 ,得到的iterator是key 为2的
map<int, string>::iterator it = su.first;
cout << " (*it).first = " << (*it).first << endl; //2
cout << " (*it).second = " << (*it).second.c_str() << endl;// jiushi2
}
unordered_set ,unordered_multiset,unordered_map,unordered_map
内部通过 哈希表实现
这些用来替代 hash_map,hash_set,hash_multiset,hash_multimap.