容器的分类
①序列(循序)容器
a.数组Array(无法扩充)
b.向量Vector(内存后面自动扩充)
c.双向队列Deque(前面和后面都可扩充)
d.双向链表List(源代码还是个环,前后都可扩充)
e.单向链表Forward-List
双向链表比单向链表耗用更多的内存,因为一个指针在32位电脑上占四个字节。
②关联式容器(适合于快速查找)
由关键字快速找值。
①Set/Multiset
内部用红黑树做的。红黑树是高度平衡二分树,能够调整左子树和右子树差不多,不会导致一边过长一边过短。
Key=Value
②Map/Multimap
Key->Value(对应)
③不定序容器(c++ 11新出的)
位置不固定,可能随时间改变。用Hash Table做的。
Unodered Set/Multiset:
Unordered Map/Multimap
附加:Hash Table
最好的做法是Separate Chaining
元素碰撞,就将其分开–>该做法是碰撞的元素放在同一个篮子里面,用链表链接起来,但不能过长,过长需要重新设置。
总结
- array、vector、deque的内存是连续的,连续的内存空间必定是支持随机访问的,其余容器则不具备该特点。其中array为固定长度,vector、deque为变长,其中vector每次增加原本大小的大小长度,vector只能单向扩充,deque可以双向扩充,每次增加一个buffer大小。vector和deque删除和插入时,后序元素会发生联动效应,因此也不适合做频发的删除和插入。
- list、forward-list为链条式结构,这种结构在插入、删除时只需要局部调整非常方便。但是遍历时只能按照链条顺序依次遍历,查找起来并不迅速,每次都增加一个结点。
- set、map背后的实现为红黑树,所以对查找具有先天的优势,但这种结构插入删除时会产生联动效应,所以并不适合做频发的删除、插入。其中各自multi版本表示内容可以重复。
测试
(一)容器array
1.随机向容器array中插入50000个元素。
array<long,ASIZE> c;//ASIZE=50000
2.对容器array中的50000个元素进行快速排序,并且二分查找法查找某个元素
qsort(c.data(),ASIZE,sizeof(long),compareLongs);//快速排序
long *pItem=(long*)bsearch(&target,(c.data()),ASIZE,sizeof(long),compareLongs);//二分查找法
3.得出排序的所需要的时间为187毫秒,即0.187秒
(二)容器vector
vector的空间增长以2的倍数增长,即由1->2->4->8->…->2n
如果不用,就会造成空间浪费。
vector.size();//返回元素的个数
vector.capacity();//返回空间的大小
当size=100,0000时,capacity=1048576
1.push_back()一百万个元素,花费3063毫秒,即3.063秒。
2.find(c.begin(),c.end(),target)
寻找一个元素,花费0毫秒。
3.
sort(c.begin(),c.end());
string *pItem=(string*)bsearch(&target,(c.data()),c.size(),sizeof(string));
花费2765毫秒,即2.765秒。
(三)容器list
双向链表
每次扩充一个结点,结点很小,效率最高,空间利用率最高,但是找起来很慢。
1.push_back() 100,0000个元素,所花时间为3265毫秒,即3.265秒
2.max_size()=3,5791,3941//??????????????
3.find(c.begin(),c.end(),target) 16毫秒
4.c.sort() 花费2312毫秒
标准库的sort()和容器的sort()
通常容器提供的sort()更快
(四)容器forward_list
单向链表,c++ 11标准新有
只有push_front() 因为只有一端,不提供另一端(太慢,就像vector)
不提供push_back(),back()
1.push_front() 100万个元素,花费3204毫秒
2.find(c.begin(),c.end(),target) 花费15毫秒
3.c.sort() 花费2656毫秒
(五)容器slist
非标准库,单向串链表,与forward_list一样,都是单向链表,用法、概念相同。
节点结构是采用继承的方式来进行组织的,而不是使用组合的方式来进行组织。
1.push_front() 100万个元素,花费3046毫秒
(六)容器deque
双向扩充,扩充时找到双倍大小的内存空间。
(buffer)分段连续,他是号称段与段之间是连续的,但这是假象,让使用者整个是连续的。
继续放元素时,分配另外一个buffer,另外一个指针指向这个buffer。
左右指针,指向左右的buffer。每次扩充一个buffer。
1.push_back() 100万元素 2704毫秒
2.max_size()=1073741823 所有容器种deque最大
3.find(c.begin(),c.end(),target) 花费15毫秒
4.::sort() 全局排序 因为deque没有自己的sort() 花费3110毫秒
关联式容器,查询特别快!
deque涵盖了stack和queue的功能,stack和queue没有实现什么数据结构,而是使用deque做底部支撑!
由于这两个容器没有自己的数据结构,所以技术上又把他们称为容器适配器。
而且不提供iterator,避免使用iterator删除修改前面的元素,因此也没有find函数,因为返回一个iterator。
(七)容器stack
先进后出。
(八)容器queue
先进先出。
(九)容器multiset
查找特别快,可以看成一个小型数据库。<使用红黑树>高度平衡树
没有push_back()和push_front()
只有insert,内部自动放在正确的位置。
1.insert() 100万个元素,时间花了6609毫秒,即6.609秒 需要排序,时间较长
2.max_size()=2,1474,8364
3.::find(c.begin(),c.end(),target) 全局函数 花费203毫秒
4.c.find(target) 容器内部函数,花费0毫秒,因为这个容器在插入时就排好序
(十)容器multimap
<使用红黑树>高度平衡树
没有push_back()和push_front()
只有insert,内部自动放在正确的位置。
c.insert(pair<type,type>(ct,ct));
不可以用下标!!因为有重复的key!
1.insert() 100万个元素,时间花了4812毫秒,即4.812秒 需要排序,时间较长
2.max_size()=1,7895,6970
3.c.find() 花费0毫秒
(十一)容器unordered_multiset
<使用hash table>
1.c.insert(type(ct)); 100万个元素,花费4406毫秒 即4.406秒
2.max_size()=357913941
3.c.bucket_count() 105,6323 篮子的个数>元素的个数,合理,有的篮子没有元素,有个篮子n个元素(n>1),而篮子一定比元素多,因为若是篮子比元素少,那么每个篮子的元素太多,循序查找,很慢(经验法则:若元素个数>篮子个数,扩充篮子的个数✖2,将原本的元素打散,重新放入篮子内)
4.load_factor() =0.94668 载重因子
5.::find() 花费109毫秒
6.c.find() 花费0毫秒
(十二)容器unordered_multimap
最适合搜寻大量元素
(十三)容器set
<使用红黑树>
key一定要时独一无二的!!不可以重复!
1.insert() 花费3922毫秒
2.size()=32768 随机放入100万个元素,符合概率(不可以重复)
(十四)容器map
<使用红黑树>
key一定要时独一无二的!!不可以重复!
1.c[key]=val;等同insert
2.size()=100,0000 随机放入100万个元素,value可以重复,key不可以重复
(十五)容器unordered_set
<使用hash table>
(十六)容器unordered_map
<使用hash table>
(十七)容器heap
使用其他容器做底部支撑,不算是真正的容器,算是容器适配器
结构
序列式容器
array(连续空间)
vector(连续空间)
-heap(以算法形式呈现 xxx_heap())
- -priority_queue
(可以说heap里头有一个vetcor,priority_queue里头有一个heap)
list(双向)
slist(单向)改名:forward_list
deque(分段连续空间)
-stack(容器适配器)
-queue(容器适配器)
关联式容器
rb_tree(非公开,红r黑b树)
-set
-map
-multiset
-multimap
hashtable(非公开)
-hash_set(非标准)unordered_set
-hash_map(非标准)unordered_map
-hash_multiset(非标准)unordered_multiset
-hash_multimap(非标准)unordered_multimap