在看堆排序的时候,涉及到完全二叉树的知识点,需要初始化堆,然后不断的调整堆;
假设原始的数据是存储在数组中,我们需要从最后一个非叶节点开始创建堆,书中一般直接给出最后一个非叶节点的下标,这里进行简单的推到。
假设数组的长度为N,为了方便计算,完全二叉树的高度从0开始,假设最下面一个完整层(即该层的结点数为2^h)的高度为k,最后一层结点个数即为n,那么则有下面的式子成立:
于是
floor()表示向下取整。
现在用N,k来表示n,
这里并没有进行化简,后面有关会相消。表示出了n后,最下面一个完整层不含子结点的个数为
ceil()表示向上取整,简单解释下,如果最后一层是不完整的,那么它的父节点的总个数为最后一层节点个数除以2向上取整,如果最后一层有4个,那么这4个叶节点是由2个父节点产生,如果有3个,也是由2个父节点产生;所以为除2向上取整。
至此,表示出了最后一层(假设为不完整层)结点个数以及最下面一个完整层中不含叶结点的个数,那么用总的结点减去上述两者之和,即为最后一个非叶结点所对应的位置,:
当N为奇数时,其对应位置为:
当N为偶数时,其对应位置为:
于是,第一个非叶结点位置为:
考虑到下标从0开始,其在数组中的位置应为计算出的值减去1,即为。
假设知道某个结点的坐标p,如何其存在左右子结点的话,那么左右子结点的坐标分别为2*p+1,2*p+2。
假设当前结点所在的高度为h且为本层的第i个结点,那么该结点到其左结点(包括左子结点)之间存在的结点个数计算如下:
该层的结点数为2^h,那么其左边共有(2^h-i)个结点,其会产生2*(i-1)个子结点,其右边共有(2^h-i)个结点,加上左子结点本身,
共有 2*(i-1)+(2^h-i)+1个结点;假设数组的下标从0开始,那么该结点本身的坐标p为
其左子结点坐标p_l为:
于是p_l=2*p+1,那么右结点坐标(假设存在的话)自然为p_r=2*p+2。