1. map 和 unordered_map 有什么区别呢?
map
和 unordered_map
都是关联容器,用于存储键值对
🍎①底层实现和有序性
map
使用红黑树实现,这保证了元素按照键的大小有序存储,元素是有序的
。unordered_map
使用哈希表实现,它根据键的哈希值来快速查找元素,因此不保证元素的顺序,元素无序
。
🍎②查找、插入、删除的时间复杂度:
map
中查找元素的复杂度为 O(log n),其中 n 是元素的数量。unordered_map
中查找元素的平均时间复杂度为 O(1),最坏情况下为 O(n),其中 n 是桶的数量,桶的数量取决于哈希函数的质量 和 负载因子。
🍎③空间占用:
- 由于
unordered_map
使用哈希表,可能会需要更多的内存空间来 存储桶和哈希值,因此在某些情况下会占用比 map 更多的内存。
2.map 和 set 的区别和实现原理
map
内部实现了⼀个红⿊树(红⿊树是⾮严格平衡的⼆叉搜索树,⽽AVL是严格平衡⼆叉搜索树),红⿊树有⾃动排序的功能,因此map
内部所有元素都是有序的,红⿊树的每⼀个节点都代表着map的⼀个元素。
因此,对于 map
进⾏的查找、删除、添加等⼀系列的操作都相当于是对红⿊树进⾏的操作。
map
中的元素是按照⼆叉树(⼜名⼆叉查找树、⼆叉排序树)存储的,特点就是左⼦树上所有节点的键值都⼩于根节点的键值,右⼦树所有节点的键值都⼤于根节点的键值。使⽤中序遍历可将键值按照从⼩到⼤遍历出来。
🍎共同点:
都是C++的关联容器,只是通过它提供的接⼝对⾥⾯的元素进⾏访问,底层都是采⽤红⿊树实现。
🍎不同点:
set
:⽤来判断某⼀个元素是不是在⼀个组⾥⾯。
map
:映射,相当于字典,把⼀个值映射成另⼀个值,可以创建字典。
🍎优点:
查找某⼀个数的时间为O(logn)
;遍历时采⽤ iterator
,效果不错。
🍎缺点:
每次插⼊值的时候,都需要调整红⿊树,效率有⼀定影响。
🍎细节
1、为什么要成倍的扩容⽽不是⼀次增加⼀个固定⼤⼩的容量呢?
采⽤成倍⽅式扩容,可以保证常数的时间复杂度,⽽增加指定⼤⼩的容量只能达到O(n)的时间复杂度。
2、为什么是以两倍的⽅式扩容⽽不是三倍四倍,或者其他⽅式呢?
考虑可能产⽣的堆空间浪费,所以增⻓倍数不能太⼤,⼀般是1.5或2;GCC是2;VS是1.5,k =2 每次扩展的新尺⼨必然刚好⼤于之前分配的总和,之前分配的内存空间不可能被使⽤,这样对于缓存并不友好,采⽤1.5倍的增⻓⽅式可以更好的实现对内存的重复利⽤。
C++并没有规定扩容因⼦K,这是由标准库的实现者决定的。
3.map、set 跟其他序列容器相比
①为什么insert之后,以前保存的iterator不会失效?
因为 map 和 set 存储的是结点,不需要内存拷⻉和内存移动。但是像 vector 在插⼊数据时如果内存不够会重新开辟⼀块内存。map 和 set 的 iterator 指向的是节点的指针,vector 指向的是内存的某个位置
②为何map和set的插⼊删除效率⽐其他序列容器⾼?
因为 map 和 set 底部使⽤红⿊树实现,插⼊和删除的时间复杂度是 O(logn),⽽向 vector 这样的序列容器插⼊和删除的时间复杂度是 O(N)