C++ STL 序列容器

C++ STL也就是C++标准模板库,它是C++标准库的一部分。STL的目的是标准化组件,它实现了常用的数据结构和算法。本次主要讲下STL的几个序列容器。

1、vector

数据结构:一段连续的线性内存空间,元素连续存放。

通过分析源码,vector就是使用了3个迭代器来表示的:

源码片段:位置指针
vector结构示意图

通过灵活运用以上三个迭代器,vector容器可以轻松的实现诸如首尾标识、大小、容器、空容器判断等几乎所有功能,如:

源码片段:成员函数

vector元素的插入:

push_back在最后插入数据
insert指定位置插入

vector元素的移除和插入同理,需要将指定元素移除后,指定元素后面的数据往前移动覆盖数据,当然,移除最后一个元素时,不需要移动拷贝数据。

结论:

1、有一段连续的内存空间,并且起始地址不变,因此能够很好的支持随即存取,即[]取值操作符;

2、由于内存是连续的,在中间进行插入和删除时会造成内存块的拷贝,另外,当vector的内存空间不够时,需要重新申请一块足够大的内存并进行数据的重新拷贝,极其影响效率。

提升效率的方法:

1、预先分配足够大小的内存,避免因频繁的执行扩容影响效率;

2、尽量使用在最后面插入新元素的方法;即尽量使用push_back,不用或少用insert、erase方法。

2、list

数据结构:双向链表


list数据结构


list结构源码
list插入过程


list插入源码


list删除过程


list删除源码

总结:

1、具有vector和deque容器所不具备的优势,它可以在常规时间内,在序列已知的任何位置插入或删除元素。

2、无法通过位置来直接访问序列中的元素,即,不能索引元素。为了访问list内部的一个元素,必须一个一个的从第一个或最后一个开始遍历元素。

3、deque

数据结构:双向队列,可以在头尾两端插入或删除元素;

形象说来,Map中控器和缓存区的关系就类似于一本字典,字典以拼音首字母为索引,拼音首字母对应字典的页数范围就类似于Map中控器节点指向一段缓存区。假如要加入新的首字母,则又在字典插入一段该首字母对应的页数,同时将页数范围记录到字典首字母对应的页数中。

Deque数据结构

迭代器里面含有4个成员,连续空间开始地址(first),结束地址(last),空间中当前元素的地址(cur)以及连续空间地址在map中的位置(node)。这里的map不是stl里的map,而是一小块连续空间,其中每一个元素都是指针,指向另一段(较大的)连续线性空间(deque储存空间的主体),当map空间已满,需要再找一块更大的空间来作为map。first、last指向node所指缓冲区的头和尾,标示出缓冲区的边界,当iterator++(node向右移动)或iterator--(由node向左移动)到边界时,为了保持其连续线,需要跳到下一个缓冲区。

map中控器

1、map中控器的大小如何确定?

map管理的节点个数是求max(8, 所需节点数+2),其中8是map的默认节点数;

所需节点数算法:元素个数/缓存区能存储的元素数 + 1;(缓存区默认是512字节的大小)

以要存储的是20个int类型的元素为例:

缓存区能存储 size = 512字节/sizeof(int) = 128个int类型的元素;

所需节点数nodeNum = 20/size + 1 = 1个节点;

则map管理的节点个数max(8, 1+2)即8个节点。

申请的map空间不够时,也需要重新配置更大的空间,将原来map里的指针拷贝过来,最后释放原来的空间。

扩容新map的分配规则:旧map.size() + max(旧map.size(),新增节点数) + 2.

若新增节点数为5,旧map的大小为8,则扩容后map的大小为:

8 + max(8,5) + 2 = 20;

2、deque数据的插入、删除是怎样的?

两端插入与删除:

以push_front为例,叙述头端插入过程:(画流程图太麻烦,直接以步骤的方式展现)

push_front(x)步骤

以pop_front为例,叙述头端删除过程:(画流程图太麻烦,直接以步骤的方式展现)


pop_front(x)步骤


后端插入删除push_back()、push_front()步骤和前述同理,理解了前端就能理解后端的,不再赘述。

双端插入和删除不需要对已有元素进行移动,因此效率非常高。

中间插入与删除:


insert(x)步骤

中间删除步骤和插入步骤类似,不再赘述。

注意:在中间插入和删除元素时,会导致指向deque元素的任何pointer、iterator失效。但是,deque的内存重分配优于vector,因为其内部结构不需要复制所有元素。

3、deque数据是否支持随机访问元素?

支持,它可以像vector一样使用[]访问任意元素,但是随机访问速度比不上vector快,因为它要内部处理堆跳转。

总结:

1、deque与vector最大的差异就是:deque是分段连续线性空间,随时可以增加一段新的空间;而vector是当内存不够时,需要重新分配、复制数据、释放原始空间。

2、比较优劣:

vector是可以快速地在最后添加删除元素,并可以快速地访问任意元素。

list是可以快速地在所有地方添加删除元素,但是只能快速地访问最开始与最后的元素

deque在开始和最后添加元素都一样快,并提供了随机访问方法。

由于deque不要求连续空间,所以可以保存的元素比vector更大,在前面和后面添加元素时都不需要移动其它块的元素,所以性能也很高。

3、如何选择:

在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,一般应遵循下面的原则:

(1)如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector;

(2)如果你需要大量的插入和删除,而不关心随即存取,则应使用list;

(3)如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque;

4、queue

数据结构:一种先进先出的队列;底层容器默认为deque容器,list也可以实现,是单端和双端的区别。(严格来说,queue其实是适配器,而不是容器,因为它是对容器的再封装)

入队出队

总结:

1、必须从一个口元素入队,另一个口元素出队。

2、不能随机存取,不支持遍历。

5、stack

数据结构:后进先出的栈;底层容器默认为deque容器,list也可以实现,是单端和双端的区别。(严格来说,stack其实是适配器,而不是容器,因为它是对容器的再封装)

stack和“撤销”操作是一样的,操作入栈,撤销时,最先出栈的不就是刚刚进行的操作吗。


入栈出栈

总结:

1、必须从同一个口入栈出栈。

2、不能随机存取,不支持遍历。

6、priority_queue

数据结构:不论先进后进,优先级最高者先出的队列。但是如何定义“优先级”取决于我们,比如在医院,病情最严重的得到最先救治,操作系统进程调度中,优先级最高的得到优先调用。其内部使用的是堆排序。

想具体了解堆排序的推荐看下:

https://www.cnblogs.com/chengxiao/p/6129630.html

说下相关概念:

堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

大顶堆小顶堆

先写个简单的例子:

priority_queue<Type, Container, Functional>,模板申明带3个参数,其中Type 为数据类型,Container为保存数据的容器,Functional 为元素比较方式。Container必须是用数组实现的容器,比如vector, deque等等,但不能用 list(原因是堆排序是通过数组构建的堆)。STL里面默认用的是vector。

大顶堆排序


小顶堆排序

为了方便理解,提出这样一个需求:公司微波炉热饭顺序按年龄来确定热饭优先级,年长者优先热饭。

热饭类,重写operator<方法


年长者优先热饭

总结:

 1、priority_queue只允许访问最顶端元素(priority_queue.top),即堆顶元素;

 2、priority_queue内部采取的最大堆的算法,每次弹出的元素都是优先级最高的元素,并且加入或弹出元素,内部元素都需要重新调整结构,即排序;

 3、由于需要计算优先级,所以如果不是基本数据类型,则必须重载operator确定优先级。


参考链接:

https://juejin.im/post/5c9de926f265da30b53eb970

https://www.cnblogs.com/xiaogege/archive/2013/04/06/STL_deque.html

http://blog.chinaunix.net/uid-13909379-id-4902042.html

https://www.jianshu.com/p/65fdd3099238

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容