堆的向上调整算法
现在我们给出一个数组,逻辑上看成是一颗完全二叉树,我们通过根节点逐渐开始向上调整算法可以将其调整为一个小堆
堆的向上调整有一个前提,那就是当前结点前面的元素已经满足成为堆的条件,这样从当前结点开始向上调整才是有意义的
思路
当我们得到一个数组,想要利用向下调整的算法将这个数组变成一个堆的形式。
那么我们就要从根节点开始也就是从下标为0的位置开始向上调整,由于此时只有一个元素,默认已经是一个堆了,也就是[0.0]范围内的元素已经满足堆的条件,此时我们在将调整范围扩大到[0,1],从下标为1的地方开始向上调整,使[0,1]范围内的元素满足称为堆的条件,再将调整范围扩大到[0,2]……直到调整范围为[0,n]时,整个数组从下标为0的地方开始到下标为n的地方都已经按照建堆的规矩排列好了,此时,堆也就建成了。
代码实现
void adjustUp(int* arr,int index)
{
while (arr[index] > arr[(index - 1) / 2])
{
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
void HeapInit(int* arr,int sz)
{
for (int i = 1; i < sz; i++)
{
adjustUp(arr, i);
}
}
时间复杂度分析
推导过程:
因此向下调整建堆的时间复杂度为O(N)
堆的向下调整算法
当我们给出一个数组,逻辑上看成是一个完全二叉树,我们通过从根节点开始向下调整算法可以把它调整为一个小堆
但是向下调整算法有一个前提,那就是左右两个子树必须也是一个堆,才能进行调整
思路
倘若从根节点开始调整,由于由于根节点的左右两个子树并不是堆,所以调整过后也无法将该数组变成一个堆
所以,我们应该从数组后面进行调整,当位于树的最后一层时,由于没有左右子树,此时向下调整是没有意义的,正确的做法是从倒数第二层最右侧的节点开始向下调整
这个倒数第二层最右侧的节点并不难求出,它其实就是最后一个节点的父节点
代码实现
void adjustDown(int* arr,int index,int sz)
{
while (index * 2 + 1 < sz)
{
int left = index * 2 + 1;
int min = left + 1 < sz && arr[left + 1] < arr[left] ? left + 1 : left;
if (arr[min] < arr[index])
{
swap(arr, min, index);
index = min;
}
else
{
break;
}
}
}
void HeapInit(int* arr,int sz)
{
for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
{
adjustDown(arr, i, sz);
}
}
时间复杂度分析
因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响结果)
推导过程:
因此向下调整建堆的时间复杂为O(N)