1.容器
容器用于容纳元素集合,并对元素集合进行管理和维护.
传统意义上的管理和维护就是:增,删,改,查.
我们分析每种类型容器时,主要分析其增,删,改,查动作实现,及复杂度.
2.堆
2.1.结构
2.1.1.图解
堆是容器类型.
采用容器内元素在线性空间连续存储的组织方式(数组也是如此).
但除此之外,基于元素的组织方式,为其抽象出了一层二叉树的逻辑关系.
以一个具体实例来说明.
上图是代表了一个可容纳12
个元素的线性空间.现在存储了8
个有效元素.堆的元素存储和数组一致.
但堆为其顺序存储的元素抽象出了一层二叉树的逻辑关系.
抽象的过程为,对顺序存储的每个元素,依次用这些元素顺序填满一颗满二叉树.
所谓满二叉树指的是,除了树最后一层,其余各层均是满的.最后一层,最后一个元素左边各个元素均是存在的.
如果观察特点可以得出以下结论,对顺序存储中索引为nIndex
的元素.
逻辑结构里,其左孩子是索引为(2*nIndex+1)
的元素,其右孩子是索引为(2*nIndex+2)
的元素.
堆针对逻辑结构又施加了一层限制.
对最大堆来说,这层限制是:对堆中任一元素,该元素需要大于等于其左孩子,其右孩子上的元素.
对最小堆来说,这层限制是:对堆中任一元素,该元素需小于等于其左孩子,其右孩子上的元素.
在以上条件均满足下,可被利用的性质是:
(1). 首个元素是所有元素中最大的(对最大堆),最小的(对最小堆).
(2). 抽象出来的二叉树由于是满二叉树,其高度为:log
以2
为底n
的对数.
2.1.2.存在一致性约束容器特点
(1). 插入无需提供位置信息.
(2). 不支持直接原地修改.一般分解为移除,添加两个过程.
(3). 由于元素值决定其位置,这类容器一般插入元素时,以std::pair<key, value>
形式插入.即元素包含键,值两部分.键用来实现一致性约束.值是此键关联的实际内容.
2.2.动作
2.2.0.建堆
意思是直接给一个连续存储的元素集合,把这些元素集合调整为符合堆性质的元素集合.
最直观当然是对集合内每个元素直接执行插入,这样所有元素插入完毕,就得到一个由这些元素构成的堆.
这里介绍另一种方式.
从集合最后一个元素反向遍历到第一个元素.
对每次遍历到的元素,让其符合:以该元素为根子树中所有元素均满足堆的性质.
下面介绍针对每个遍历到元素的调节过程.
不妨假设我们现在遍历到的元素是p
.假设是最小堆.
由于我们之前每次遍历时,均按上述要求.所以,p
的左孩子为根子树中所有元素此时均满足堆的性质.p
的右孩子为根子树也是如此.此刻子树中唯一可能不满足要求的就是p
和其左,右孩子.
若p
小于其左,右孩子上的元素,则无需调节.
否则,找到三者中最小元素q
.交换此q
和p
的位置.
交换后对p
和其左右孩子来说堆的性质得到满足.但对q
来说,由于此位置元素变大了.所以,此刻子树中只有q
与其左右孩子可能不满足堆的性质.这样,我们虽然未立即解决问题.但将问题转化为了一个同类型问题.
由于树的高度有限,每次转化后我们在更低一层子树上再次处理同类问题.
故,即使最坏下,至多经过有限次调节,也可解决问题.
2.2.1.增
堆中插入新元素.对于存在一致性约束的容器,元素插入到容器后,需经历调节过程.不需要也无法在指定位置实现插入.具体位置依赖调节过程.
插入过程可描述为:
(1). 将新元素放在数组尾后位置.
(2). 这样,此时数组对应的二叉树中,只有新节点p
和其父亲q
可能不满足堆的性质.
(3). 我们分析最小堆场景.比较p
和q
,若q
中元素小于等于p
,则无需调节.
(4). 若q
中元素大于p
,交换p
,q
内元素.交换后,由于q
位置元素变小了.所以,q
和其父节点可能不满足堆的性质.p
位置元素变小了.故,以p
和其左右孩子必然满足堆的性质.
这样,我们虽然未立即解决问题,但将问题转化了.由于树的高度有限,故,最坏下也能经过有限次迭代结束调节过程.
2.2.2.删
堆由于其性质,一般移除限定只能移除首个元素.我们分析移除首个元素过程.
删除首元素可描述为:
(1). 交换尾元素和首元素.递减有效元素数量.
(2). 我们分析最小堆场景.此时,首元素p
相比原来变大了.此时,整棵树中只有p
和其左右孩子可能不满足堆的性质.
(3). 我们寻找节点p
,其左孩子lp
,其右孩子rp
,三者中最小元素及其位置.
(4). 若最小元素是p
,则无需调节.
(5). 若最小元素是某个孩子cp
(要么lp
,要么rp
).交换cp
和p
上元素.这样,交换后p
和其左右孩子此时满足了堆的性质.但cp
上元素变大了,所以,此时cp
和其左右孩子可能不满足堆的性质.
这样,我们虽然未能立即解决问题.但将问题转化了.由于树的高度有限,故,最环下也能经过有限次迭代结束调节过程.
2.2.3.查
堆一般只提供访问首元素方法即可.
访问首元素直接基于索引访问即可.
2.2.4.改
对于存在一致性约束的容器,一般不会允许直接修改元素的值.
此类操作一般分解为:删除老元素,添加新元素两个过程来实现.
删除非首元素,类似删除首元素过程.只不过此时先交换删除位置元素和尾部元素.再从此开始进入调节流程.
2.3.时间复杂度
评价容器的依据一个是其占据的线性空间,一个是操作执行的时间复杂度.
堆的各个操作时间复杂度为:
(1). 增:Θ(log
以2
为底n
的对数)
(2). 删:Θ(log
以2
为底n
的对数)
(3). 查:Θ(1
) (索引访问)
(4). 改:Θ(1
) (log
以2
为底n
的对数)