heap
堆并不属于
STL
容器组件,它是个幕后英雄,扮演
priority_queue
的助⼿,
priority_queue
允许⽤户以任何次序
将任何元素推⼊容器内,但取出时⼀定是从优先权最⾼(数值最⾼)的元素开始取。⼤根堆(
binary max heap
)
正具有这样的性质,适合作为
priority_queue
的底层机制。
⼤根堆,是⼀个满⾜每个节点的键值都⼤于或等于其⼦节点键值的⼆叉树(具体实现是⼀个
vector
,⼀块连续空
间,通过维护某种顺序来实现这个⼆叉树),新加⼊元素时,新加⼊的元素要放在最下⼀层为叶节点,即具体实现
是填补在由左⾄右的第⼀个空格(即把新元素插⼊在底层
vector
的
end()
),然后执⾏⼀个所谓上溯的程序:将新
节点拿来与 ⽗节点⽐较,如果其键值⽐⽗节点⼤,就⽗⼦对换位置,如此⼀直上溯,直到不需要对换或直到根节点
为⽌。当取出⼀个元素时,最⼤值在根节点,取⾛根节点,要割舍最下层最右边的右节点,并将其值᯿新安插⾄最
⼤堆,最末节点放⼊根节点后,进⾏⼀个下溯程序:将空间节点和其较⼤的节点对调,并持续下⽅,直到叶节点为
⽌。
priority_queue
底层时⼀个
vector
,使⽤
heap
形成的算法,插⼊,获取
heap
中元素的算法,维护这个
vector
,以达到允许⽤户
以任何次序将任何元素插⼊容器内,但取出时⼀定是从优先权最⾼(数值最⾼)的元素开始取的⽬的。
slist
:
STL list
是⼀个双向链表,
slist
是⼀个单向链表。
2
、
vector
使⽤的注意点及其原因,频繁对
vector
调⽤
push_back()
性
能影响
使⽤注意点:
注意插⼊和删除元素后迭代器失效的问题;
清空
vector
数据时,如果保存的数据项是指针类型,需要逐项
delete
,否则会造成内存泄漏。
频繁调⽤
push_back()
影响:
向
vector
的尾部添加元素,很有可能引起整个对象 存储空间的᯿新分配,᯿新分配更⼤的内存,再将原数据拷⻉
到新空间中,再释 放原有内存,这个过程是耗时耗⼒的,频繁对
vector
调⽤
push_back()
会导致性能的下降。
在
C++11
之后,
vector
容器中添加了新的⽅法:
emplace_back()
,和
push_back()
⼀样的是都是在容器
末尾添加⼀个新的元素进去,不同的是
emplace_back()
在效率上相⽐较于
push_back()
有了⼀定的提升。
emplace_back()
函数在原理上⽐
push_back()
有了⼀定的改进,包括在内存优化⽅⾯和运⾏效率⽅⾯。内存
优化主要体现在使⽤了
就地构造(直接在容器内构造对象,不⽤拷⻉⼀个复制品再使⽤)
+
强制类型转换
的⽅法来
实现,在运⾏效率⽅⾯,由于省去了拷⻉构造过程,因此也有⼀定的提升。
3
、
map
和
set
有什么区别,分别⼜是怎么实现的?
map
和
set
都是
C++
的关联容器,其底层实现都是红⿊树(
RB-Tree
)。
由于
map
和
set
所开放的各种操作接⼝,
RB-tree
也都提供了,所以⼏乎所有的
map
和
set
的操作⾏为,都只是
转调
RB-tree
的操作⾏为。
map
和
set
区别在于:
(
1
)
map
中的元素是
key-value
(关键字
—
值)对:关键字起到索引的作⽤,值则表示与索引相关联的数据;
Set
与之相对就是关键字的简单集合,
set
中每个元素只包含⼀个关键字。
(
2
)
set
的迭代器是
const
的,不允许修改元素的值;
map
允许修改
value
,但不允许修改
key
。其原因是因为
map
和
set
是根据关键字排序来保证其有序性的,如果允许修改
key
的话,那么⾸先需要删除该键,然后调节平衡,
再插⼊修改后的键值,调节平衡,如此⼀来,严᯿破坏了
map
和
set
的结构,导致
iterator
失效,不知道应该指向改
变前的位置,还是指向改变后的位置。所以
STL
中将
set
的迭代器设置成
const
,不允许修改迭代器的值;⽽
map
的
迭代器则不允许修改
key
值,允许修改
value
值。
(
3
)
map
⽀持下标操作,
set
不⽀持下标操作。
map
可以⽤
key
做下标,
map
的下标运算符
[ ]
将关键码作为下标去
执⾏查找,如果关键码不存在,则插⼊⼀个具有该关键码和
mapped_type
类型默认值的元素⾄
map
中,因此下标
运算符
[ ]
在
map
应⽤中需要慎⽤,
const_map
不能⽤,只希望确定某⼀个关键值是否存在⽽不希望插⼊元素时也不
应该使⽤,
mapped_type
类型没有默认值也不应该使⽤。如果
find
能解决需要,尽可能⽤
find
。