1、堆的定义
首先,堆是完全二叉树(二叉树:每个结点有两个子节点),它满足如下性质:
(1)任何一个结点都不大于其父结点,这种堆称为大顶堆 或者 任何一个结点都不小于其父结点,这种堆称为小顶堆
(2)需要是完全二叉树,最后一层在最左侧
2、用数组来存储二叉树
首先给二叉树从左至右,从上至下来给每个结点编号,如此编号后,子结点的左结点是父结点的二倍,子结点的右结点是父结点的2倍加1。
之后,就可以用如下图的数组来存储这个堆了,该数组从1开始计数,以符合这个堆的索引。
这里要解释一下的是:
第i个结点的父结点的索引计算公式是:parent(i)=i/2 这里的"/"是计算机里的除号,取整符。
第i个结点的子结点分别是:left child(i)=2i;right child(i)=2i+1。
根据这个公式,就可以在数组中索引相应的结点数据了
3、二叉树shiftUp操作
作用:将插入的元素放到堆的最后一个叶子,并且对堆进行排序,使其成为最大堆。如下图所示:
方法:由于没插入此元素之前,这个堆是最大堆,新插入的元素要做的就是将它和父结点比较,如果大于父结点,就和父结点调换位置,接下来继续比较调换过父结点和其上一层父结点。直至形成最大堆
代码如下:
void __shiftup(int k)
{
while (k>1 && data[k / 2]<data[k])
{
std::swap(data[k / 2], data[k]);
k = k / 2;
}
}
void insert(T item)
{
//如果堆里的数据超过容纳总量,则报错
assert(capcity > count + 1);
//将元素插入数组
data[count + 1] = item;
count++;
//将元素插入完全二叉树,传入参数是数组的最后一个索引
__shiftup(count);
}
在第k个节点插入元素,并对其进行shiftup操作。
4、shiftdown操作
作用:从堆中取出一个元素后,将堆排序成最大堆。
从堆中取出元素的方法:先将第一个元素取出,然后将最后一个元素放到第一个元素位置。同时count–。然后将第一个元素与两个子节点比较,如果子节点比该元素大,则交换父结点和子节点的元素。之后继续比较交换过的这个子节点和该子节点下面的子节点。
如下图:先取出data[1],然后将data[11]和data[1]交换
交换后形成下图的这个二叉树。
由于该二叉树的data[1]<子节点的最大值data[2],所以需要将data[1]与data[2]交换
接下来继续比较data[2]他的两个子节点data[4],data[5]。这里,需要交换data[2]和data[5]。形成下图所示的树。
代码如下:
void __shiftdown(int k)
{
//如果左子节点存在,则需要一直执行shiftdown操作
while (2 * k <= count)
{
//如果存在右子节点,比较左右子节点,将大的节点的索引放到j里面
int j = 2 * k;
if (2 * k + 1 <= count&&data[j]<data[2 * k + 1])
{
j = 2 * k + 1;
}
//如果父结点小于左子节点,则调换位置
if (data[k]<data[j])
{
swap(data[k], data[j]);
}
else
{
break;
}
}
}
T extraMax()
{
T result = data[1];
data[1] = data[count];
count--;
__shiftdown(1);
return result;
}