108 C++ STL 容器 -- deque 和stack,queue,list,forward_list, set/map, unordered_set, unordered_mulitset

先来回顾一下,STL的组成 

vector :

deque:(double -ended queue)顺序容器,双端队列,双向开口。

实际上是这样的,数据的存储是分段的,翻看C++的文档,说的是 deque 的元素不是相接存储

不管是从头插入,还是从尾插入 都很快

如果往中间插入元素,那么涉及到移动其他元素,效率会比较低

std::deque(double-ended queue,双端队列)是有下标顺序容器,它允许在它的首尾两端快速插入及删除。另外,在 deque 任一端插入或删除不会使指向其余元素的指针或引用失效。

与 std::vector 相反,deque 的元素不是相接存储的:典型实现用单独分配的固定尺寸数组的序列,外加额外的登记,这表示下标访问必须进行二次指针解引用,与之相比 vector 的下标访问只进行一次。

#include <queue>

在标头 <deque> 定义

template<

    class T,
    class Allocator = std::allocator<T>

> class deque;
(1)
namespace pmr {

    template< class T >
    using deque = std::deque<T, std::pmr::polymorphic_allocator<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.

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值