C++ STL容器 底层数据结构

一、前言

1. 总的来说,STL包括几个部分:容器,算法(泛型算法),迭代器三个主要部分(当然还包含仿函数,适配器等其他部分)

2. 容器包含两大类:顺序容器、关联容器

顺序容器 元素是按它们在容器中的位置来顺序保存和访问的。

  • 3 种 -- 可变长动态数组 vector、双端队列 deque、双向链表 list

关联容器 元素是按关键字来保存和访问的。

  • 4 种 -- set、multiset、map、multimap
  • 默认情况下,关联容器中的元素是从小到大排序(或按关键字从小到大排序)

其他

  • 3 种容器适配器:栈 stack、队列 queue、优先级队列 priority_queue

二、顺序容器

1、vector

其底层数据结构是数组,由于数组的特点,vector也具有以下特性:
a)、O(1)时间的快速访问;
b)、顺序存储,所以插入到非尾结点位置所需时间复杂度为O(n),删除也一样;
c)、扩容规则:

当我们新建一个vector的时候,会首先分配给他一片连续的内存空间,如std::vector<int> vec,当通过push_back向其中增加元素时,如果初始分配空间已满,就会引起vector扩容,其扩容规则跟编译器有关,1.5 倍或 2 倍扩容:
首先重新申请一个 1.5 / 2 倍大的内存空间;
然后将原空间的内容拷贝过来;
最后将原空间内容进行释放,将内存交还给操作系统

d)、注意事项:
根据vector的插入和删除特性,以及扩容规则,我们在使用vector的时候要注意,在插入位置和删除位置之后的所有迭代器和指针引用都会失效,同理,扩容之后的所有迭代器指针和引用也都会失效。

2、list  deque

list的底层数据结构为双向链表,特点是支持快速的增删。内存地址不连续
queue为单向队列,为先入先出原则。由多个连续内存块构成,deque是 list 和 vector 的兼容
deque为双向队列,其对比queue可以实现在头尾两端高效的插入和删除操作

3、下面是选择顺序容器类型的一些准则

1. 如果我们需要随机访问一个容器则 vector 要比 list 好得多 。
2. 如果我们已知要存储元素的个数则 vector 又是一个比 list 好的选择。
3. 如果我们需要的不只是在容器两端插入和删除元素则list显然要比 vector 好
4. 除非我们需要在容器首部插入和删除元素否则 vector 要比 deque 好。
5. 如果只在容易的首部和尾部插入数据元素,则选择 deque.
6. 如果只需要在读取输入时在容器的中间位置插入元素,然后需要随机访问元素,则可考虑输入时将元素读入到一个 List容器,接着对此容器重新排序,使其适合顺序访问,然后将排序后的 list 容器复制到一个 vector 容器中

三、关联容器

1、map & multimap & unordered_map & unordered_multimap

map与multimap底层数据结构

  • 有序的
  • map 一对一 key-value
  • multimap  多对多 key-value
  • 区别在于,multimap允许关键字重复,而map不允许重复。
  • 底层数据结构均为红黑树,关于红黑树的理解可以参考教你透彻了解红黑树一文。
  • 根据红黑树的原理,map与multimap可以实现 O(lgn) 的查找,插入和删除。
  • 红黑树提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间

unordered_map 与unordered_multimap底层数据结构

  • 无序的
  • 底层实现为 hash table,因此其查找速度非常的快,理论( 理想无碰撞的情况下 ) 上达到了O(n)
  • 存储方式 根据 散列值组织成桶 以允许通过它们的键值直接快速访问单个元素(具有常数平均时间复杂度
  • 每个桶保存零个或多个元素,容器的性能依赖于哈希函数的质量和桶的数量和大小
  • 哈希表的建立比较耗费时间

【注】:mulitimap

如果multimap中有多个元素具有相同的关键字,则这些关键字在容器中会相邻存储

获取多个相同关键字对应值的方法 如下:

multimap<int, string> mMap;
typedef multimap<int, string>::iterator int_multimap;

// equal_range(key) 返回 key 对应的所有元素的起始和结束迭代器.
pair<int_multimap, int_multimap> p = mMap.equal_range(4);

for (int_multimap k = p.first; k != p.second; k++)
{
	cout << k->second << endl;
}
//authors是一个multimap容器 
string search_item("Alain"); 
int numbers=authors.count(search_item); 
auto it=authors.find(search_item); 
while(numbers) 
{ 
    cout<<iter->second<<endl; 
    ++it; 
    numbers--; 
}

相关接口操作

1.桶接口

m.bucket_count()        // 正在使用的桶的数目
m.max_bucket_count()    // 容器能容纳的最多的桶的数量
m.bucket_size(n)        // 第n个桶中有多少个元素
m.bucket(k)             // 关键字为k的元素在哪个桶

 2.桶迭代

local_iterator            // 可以用来访问桶中元素的迭代器类型
const_local_iterator      // 桶迭代器的const版本
m.begin(n)、m.end(n)      // 桶n的首元素迭代器和尾后迭代器(n是什么类型?)
m.cbegin(n)、m.cend(n)    // 与前两个函数类似,但返回const_local_iterator

3.哈希策略

//每个桶的平均元素数量,返回float值
m.load_factor() 
//m试图维护的平均桶大小,返回float值,要求创建的新桶的load_factor<=max_load_factor         
m.max_load_factor() 
//重新存储,使得bucket_count>=n,且bucket_count>size/max_load_factor         
m.rehash(n)  
//重新存储,使得m可以保存n个元素且不必rehash 
m.reserve(n)            

2、set & multiset & unordered_set & unordered_multiset

set 系与 map 系的区别

  • map中存储的是<key-value>,而 set 可以理解为关键字即值,即只保存关键字的容器

set & multiset底层数据结构

  • 红黑树,能实现O(lgn)的查找,插入,删除操作

unordered_set & unordered_multiset

  • 底层实现为hash table

注:所有 有序 关联容器 数据是按 关键字的 字典顺序 进行存储存放的

四、其他

1、 priority_queue 优先级队列

优先级队列相当于一个有权值的单向队列queue,在这个队列中,所有元素是按照优先级排列的。

priority_queue根据的处理规则来调整元素之间的位置,关于堆的原理,可以参考

根据堆的特性,优先级队列实现了取出最大最小元素时间复杂度为 O(1) ,对于插入和删除,其最坏情况为O(lgn)。

详细用法见 https://blog.csdn.net/xiaoquantouer/article/details/52015928

发布了87 篇原创文章 · 获赞 46 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/LearnLHC/article/details/89552420
今日推荐