引入

最近看了《STL源码剖析》的第 4 章和第 5 章,介绍了 C++ STL 中的序列式容器和关联式容器,本文将总结序列式容器的基础概念,不会详细它们的实现原理(想知道自个儿看书吧,我目前只想了解它们的基本原理,用的时候心里有数就行)。

容器概念

容器是存储数据的地方。C++ STL 容器是一些常见数据结构的实现。任何数据结构都是为了特定的算法服务的。

数据结构:数据的特定排列方式。

根据数据在容器中的排列方式,将容器分为序列式容器关联式容器

接下来将介绍序列式容器:array, vector, list, deque 以及它们的适配器:stack, queue, heap, priority_queue

序列式容器与容器适配器

基础容器

STL 中的底层容器,可以作为其他容器的底层结构(array 除外)。

1.array

  1. 内置的静态数组类型,空间大小指定后不能改变,元素存储在连续的线性内存空间,不具备动态扩容的能力,实际应用中几乎没有。

  2. 其迭代器类型为 Random Access Iterator 随机访问迭代器。

2.vector

  1. 动态数组,元素存储在连续的线性内存空间,其空间可以动态缩小或扩大,实际应用中非常普遍。

  2. 动态扩容策略:申请更大的新空间(一般是旧空间大小的 2 倍),进行旧数据迁移,释放旧空间,\(O(n)\) 线性时间开销。

  3. 动态缩容策略:只需要将表达 vector 数据结构的指针前移即可,\(O(1)\) 时间开销。

  4. 为了避免频繁发生扩容,vector 有容量的概念,即它的实际大小比客户端需求量更大一些。

  5. 引起 vector 内存空间重新配置的操作(如插入、删除操作),会导致之前定义的迭代器失效。

  6. 其迭代器类型为 Random Access Iterator 随机访问迭代器,支持算术运算。

3.list

  1. 双向循环链表,元素存储在非线性内存空间,实际应用中非常普遍。
  2. list 不会重新配置空间,因此只有被删除元素的迭代器会失效,其他原先的迭代器不会失效。
  3. 其迭代器类型为 Bidirectional Iterator 双向迭代器,只支持自增(++)和自减(--)运算。

4.deque

  1. 双端队列,采用二级结构实现。一级结构称为中控器,是一个元素均为指针的数组,每个指针指向一段连续的线性空间,这段空间即为二级结构,真正存储数据的地方。

  2. deque 的迭代器实现营造了一种“它是连续空间”的假象,其实它只是“一段一段的定量连续空间”。

  3. deque 的扩容策略说起来简单:如果一级结构中控器仍有空间,就增加一个指针,指向一段新的连续空间用于存放新元素;否则申请更大的空间迁移一级结构的指针,然后如前所述。

  4. deque 没有容量概念,因为如第 3 点所述,它可以随时申请一段新空间与旧空间“拼接”起来,不会发生“申请新空间 -> 迁移元素 -> 释放旧空间”(指的是二级结构即真正的数据不会发生迁移,一级结构中的指针还是会发生迁移的)。

  5. 书中提到 deque 的迭代器比较复杂,若需要对 deque 排序,最好借助 vector 完成。

  6. 其迭代器类型为 Random Access Iterator 随机访问迭代器,支持算术运算。

5.补充

在新的 C++ 标准中增加了 forward_list 单向链表,应该也算基础容器吧,但它的应用限制太多,只有在特定场合下才能使用。

容器适配器

以某种容器作为底层结构,改变其接口,使之符合某种特性。

1.stack

  1. stack 栈的特性是“后进先出”,默认采用 deque 作为底层结构。

  2. 其实 vectorlist 也可以作为底层结构,可以根据应用场景分别测试这 3 种底层结构的性能差异进行选择。

  3. stack 没有迭代器,因为提供迭代器会破坏它“后进先出”的特性。

2.queue

  1. queue 队列的特性是“先进先出”,默认采用 deque 作为底层结构。

  2. 其实 vector 和 list 也可以作为底层结构,但是显然不应该用 vector,因为 vector 删除首元素的时间开销是 \(O(n)\),同样的操作 list 只要 \(O(1)\) 时间,因此实际应用中只需要测试 deque 和 list 作为 queue 底层结构时的性能差异。

  3. queue 没有迭代器,因为提供迭代器会破坏它“先进先出”的特性。

3.heap

  1. 我倾向于把 heap 归类为容器适配器,因为其依赖底层结构 vector 存储数据。众所周知的一个小技巧,使用数组表示堆,可以通过下标快速定位一个节点的父节点和子节点。

  2. STL 中 heap 采用隐式表述的方式,不开放给外部使用,而是通过 heap 作为底层结构实现 priority_queue 优先队列开放给外部使用。

  3. heap 算法中有一个 make_heap 算法,其作用是将一个 vector 中的元素进行调整使之符合堆特性。在 make_heap 算法中需要调用一个 perlocate down 算法下拉调整每个节点(在 vector 中从后往前寻找第一个非叶子节点开始),此时默认采用 < 小于比较操作,即较小的节点被下拉了,那么较大的节点自然成为父节点,因此默认情况下是最大堆。

  4. heap 没有迭代器,不提供遍历功能,因为 heap 是完全二叉树,其元素需要遵循完全二叉树的排列规则。

4.priority_queue

  1. 优先队列,默认采用 vector 作为底层结构,且默认采用 max_heap 实现

  2. 根据上述 heapmake_heap 算法所述,采用 < 小于比较操作得到的是最大堆,相反采用 > 大于比较操作得到的是最小堆。

#include <`queue`>
int main() {
priority_`queue`<int, vector<int>, less<int>> pq1; // 最大堆
priority_`queue`<int, vector<int>, greater<int>> pq2; // 最小堆
return 0;
}

最后

如果你有疑惑,欢迎评论,我尽可能回复!

如果本文对你有帮助,点个赞吧,这是我坚持的动力!

STL序列式容器使用注意、概念总结的更多相关文章

  1. STL序列式容器学习总结

    STL序列式容器学习总结 参考资料:<STL源码剖析> 参考网址: Vector: http://www.cnblogs.com/zhonghuasong/p/5975979.html L ...

  2. STL——序列式容器

    一.容器概述与分类 1. STL容器即是将运用最广的一些数据结构实现出来.常用的数据结构有array, list, tree, stack, queue, hash table, set, map…… ...

  3. 数据结构-STL序列式容器总结

    根据序列在容器中的排列特性,将常见数据结构分为:序列式容器和关联式容器. 常见序列式容器有 1.array(build-in)c++內建 2.vector 3.heap(以算法方式呈现) 4.prio ...

  4. STL序列式容器

    1.vector      空间运用的灵活性.      实现技术——关键是对大小的控制以及重新配置时的数据移动效率.      配置新空间.数据移动.释还旧空间      erase(int pos ...

  5. STL源码剖析——序列式容器#1 Vector

    在学完了Allocator.Iterator和Traits编程之后,我们终于可以进入STL的容器内部一探究竟了.STL的容器分为序列式容器和关联式容器,何为序列式容器呢?就是容器内的元素是可序的,但未 ...

  6. STL源码剖析读书笔记--第四章--序列式容器

    1.什么是序列式容器?什么是关联式容器? 书上给出的解释是,序列式容器中的元素是可序的(可理解为可以按序索引,不管这个索引是像数组一样的随机索引,还是像链表一样的顺序索引),但是元素值在索引顺序的方向 ...

  7. STL源码剖析之序列式容器

    最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...

  8. STL源码剖析:序列式容器

    前言 容器,置物之所也.就是存放数据的地方. array(数组).list(串行).tree(树).stack(堆栈).queue(队列).hash table(杂凑表).set(集合).map(映像 ...

  9. STL学习笔记(序列式容器)

    Vector Vector是一个动态数组. 1.Vector的操作函数 构造.拷贝和析构 vector<Elem> c //产生一个空vector ,其中没有任何元素 vector< ...

  10. 7.5 C++基本序列式容器

    参考:http://www.weixueyuan.net/view/6402.html 总结: vector可以理解为可以在两端插入.删除数据的数组,它提供了丰富的成员函数,用于操作数据. begin ...

随机推荐

  1. ES6 学习笔记(八)基本类型Symbol

    1.前言 大家都知道,在ES5的时候JavaScript的基本类型有Number.String.Boolean.undefined.object.Null共6种,在es6中,新增了Symbol类型,用 ...

  2. 最长不下降子序列(线段树优化dp)

    最长不下降子序列 题目大意: 给定一个长度为 N 的整数序列:A\(_{1}\),A\(_{2}\),⋅⋅⋅,A\(_{N}\). 现在你有一次机会,将其中连续的 K 个数修改成任意一个相同值. 请你 ...

  3. 达梦-DBLINK数据库链接

    aliases: [达梦 DBlink] tags: [数据库,DM,Blog] link: date: 2022-09-06 说明:DM-Oracle指的是在DM中创建链接至Oracle的Dblin ...

  4. perl中sprintf函数的用法

    对于某些字符串,需要输入为特定的格式,通过sprintf可以很方便的完成,不需要专门进行其他处理. 转载 perl中sprintf函数的使用方法.

  5. 13.django-admin组件

    Django内置了一个强大的组件叫Admin,提供给网站管理员快速开发运营后台的管理站点,下面通过案例进行操作 1.创建模型类 模型类如下: from django.db import models ...

  6. JS图片放大镜功能实现

    JS图片放大镜功能实现 技术关键点 1.左侧和上侧距离,在一个水平位置和垂直位置中有我们可以挪动的区域,就是原图片区域,鼠标挪动位置是一个块状位置,他的左侧和上侧距离浏览器上侧和左侧分别有一个长度,我 ...

  7. 基于python的数学建模---洛伦兹线与数值解

    import numpy as np from scipy.integrate import odeint from mpl_toolkits.mplot3d import Axes3D import ...

  8. 错误“AxImp.exe”已退出,代码为 -1163019603

    最近调试项目时突然出现错误"AxImp.exe"已退出,代码为 -1163019603 发现引用中的组件出现了一个感叹号 经过核对是锐浪报表的组件出现了问题,尝试打开报表设计器也无 ...

  9. 动态规划篇——DP问题

    动态规划篇--DP问题 本次我们介绍动态规划篇的DP问题,我们会从下面几个角度来介绍: 区间DP 计数DP 树状DP 记忆化搜索 区间DP 我们通过一个案例来讲解区间DP: /*题目展示*/ 题目名: ...

  10. 目标检测模型的评价标准-AP与mAP

    目录 目录 目录 前言 一,精确率.召回率与F1 1.1,准确率 1.2,精确率.召回率 1.3,F1 分数 1.4,PR 曲线 1.4.1,如何理解 P-R 曲线 1.5,ROC 曲线与 AUC 面 ...