->Gitee源代码点击这里<-
堆是特殊的二叉树,其性质为:
①堆中的某个结点总是不大于或不小于其子结点
②堆总是完全二叉树。(第1到第n-1层为满,最后一层从左往右结点连续)
我们经常用顺序结构的数组来实现二叉树
对于一个普通的数组int a[10] = {18,29,42,8,50,9,22,88,19,74};
如果该数组表示的是二叉树,则其逻辑结构为
而目前该二叉树还能称为堆,所以我们要借助一些算法来把一个表示二叉树的数组变成堆。
一、向下调整算法
当父节点的子树已经是大堆或者小堆,而整个二叉树还不满足堆的条件时,则需要向下调整算法来把二叉树变成堆
如图,根节点18的子树都符合大堆的条件(父节点>=子结点),但二叉树本身还不是大堆,这是我们就需要利用向下调整算法来将该二叉树调整为大堆,即:
调整思路:
以二叉树{18,29,42,8,28,9,22,3,4,19}
为例
从需要调整的那个结点开始,把该节点视作父节点,比较其子节点的大小,取大的那个和父结点比较,如果大于父节点,则交换两个结点的值
图中要调整的是结点18 ,比较其两个子节点29 和42的大小,42较大,再比较42和18,42较大,交换42和18
此时42变为根结点,18变为右子树的父节点。如下图:
此时二叉树仍然不满足大堆的条件,以18为父节点的右子树还符合大堆的条件,仍需要向下调整,重复上述步骤,交换18 和 22:
得到下图结果:
此时二叉树已经调整为大堆
其用数组表示为:{42,29,22,8,28,9,18,3,4,19};
我们借助该例子来写出大堆的向下调整算法的代码实现
实现代码时,我们要了解以下内容:
a. 父节点坐标parent和子节点的关系为:
右子结点坐标 right= parent*2+2;
左子节点坐标 left = parent*2+1;
b. 设计算法函数时,我们不但要将数组传给函数,还要告诉函数从那个结点开始调整,即参数还需要一个坐标变量;同时调整过程是一个循环的动作,还需要传入数组的长度控制循环的停止
c. 向下调整的过程中(以调大堆为例),本质是就是不断地调整父节点和子节点的关系;当父节点比两个子结点中最大的那个结点还要小时,则说明还需要调整;反之则已经调整完成
d. 调整过程中可能会出现一个父节点只有一个子节点的情况,如上图中的[4]结点和[9]结点,此时则要注意细节,防止越界访问
void Swap(int* a, int* b) //交换函数
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void AdjustDown(int* a, int pos,int size)
{
int parent = pos;
int node = parent * 2 + 1; //先默认左节点是两个结点中较大的一个
while (parent * 2