二叉堆
- 二叉堆自我调整
 - 插入节点(上浮)
 - 删除节点(下沉)
 - 构建二叉堆(所有非叶子节点依次“下沉”)
 
二叉堆本质上是一种完全二叉树,它分为两个类型。
- 最大堆
 - 最小堆
最大堆的任何一个父节点的值,都大于或等于它左、右孩子\节点的值

最小堆的任何一个父节点的值,都小于或等于它左、右孩子节点的值。

 
二叉堆的根节点叫作堆顶。
 最大堆和最小堆的特点决定了:最大堆的堆顶是整个堆中的最大元素;最小堆的堆顶是整个堆中的最小元素。
二叉堆自我调整
堆的自我调整,就是把一个不符合堆性质的完全二叉树,调整成一个堆。
- 插入节点
 - 删除节点
 - 构建二叉堆
 
插入节点(上浮)
当二叉堆插入节点时,插入位置是完全二叉树的最后一个位置。
 这时,新节点的父节点5比0大,显然不符合最小堆的性质。于是让新节点“上浮”,和父节点交换位置。
 继续用节点0和父节点3做比较,因为0小于3,则让新节点继续“上浮”。
 继续比较,最终新节点0“上浮”到了堆顶位置。
    /**
     * 插入元素的位置是数组的最后一个位置。
     * 插入(上浮)
     * @param array
     */
    public static void upAdjust(int[] array){
        //插入位置都是在数组的末尾,所以array.length-1就是当前插入位置的数组下标.
        int childIndex = array.length-1;
        //左孩子的父节点下标
        int parentIndex = (childIndex-1)/2;
        //temp 保存插入的叶子节点值,用于最后的赋值
        int temp = array[childIndex];
        while (childIndex>0&&temp<array[parentIndex]){
            //无须真正交换,单向赋值即可
            array[childIndex] = array[parentIndex];
            //在和上一个父节点比较。。。
            childIndex = parentIndex;
            //不管上一个节点是左孩子还是右孩子,通过(parentIndex-1)/2都可找到父节点的父节点。。
            parentIndex = (parentIndex-1)/2;
        }
        //找到childIndex,把值temp插入
        array[childIndex] = temp;
    }
 

 
 
 
删除节点(下沉)
所删除的是处于堆顶的节点。
 把堆的最后一个节点10临时补到原本堆顶的位置。
 /**
     * 删除(下沉)
     * @param array    待调整的堆
     * @param parentIndex   要“下沉”的父节点
     * @param length   堆的有效大小
     */
    public static void downAdjust(int[] array,int parentIndex,int length){
        //temp 保存需要下沉的父节点值,用于最后的赋值
        int temp = array[parentIndex];
        //找到父节点的左孩子
        int childIndex = 2*parentIndex+1;
        while (childIndex<length){
            // 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子(需要和左右孩子中最小的交换)
            if(childIndex+1<length&&array[childIndex+1]<array[childIndex]){
                    //定位右孩子
                    childIndex++;
            }
            // 如果父节点小于的任何一个孩子的值,则直接跳出(没有交换的必要了)
            if(temp<=array[childIndex]){
                break;
            }
            无须真正交换,单向赋值即可
            array[parentIndex] = array[childIndex];
            //在基于现在往下找。。
            parentIndex = childIndex;
            childIndex = 2*childIndex+1;
        }
        //最后在将下沉的父节点赋值进去
        array[parentIndex] = temp;
    }
 

 
 让暂处堆顶位置的节点10和它的左、右孩子进行比较,如果左、右孩子节点中最小的一个(显然是节点2)比节点10小,那么让节点10“下沉”。
 
 
构建二叉堆(所有非叶子节点依次“下沉”)
构建二叉堆,也就是把一个无序的完全二叉树调整为二叉堆,本质就是让所有非叶子节点依次“下沉”。
    public static void buildHeap(int[] array){
        // 从最后一个非叶子节点(array.length-2)/2)开始,依次做“下沉”调整
        for (int i = (array.length-2)/2;i>=0;i--){
            downAdjust(array,i,array.length);
        }
    }
 
其中:
/**
     * 删除(下沉)
     * @param array    待调整的堆
     * @param parentIndex   要“下沉”的父节点
     * @param length   堆的有效大小
     */
    public static void downAdjust(int[] array,int parentIndex,int length){
        //temp 保存需要下沉的父节点值,用于最后的赋值
        int temp = array[parentIndex];
        //找到父节点的左孩子
        int childIndex = 2*parentIndex+1;
        while (childIndex<length){
            // 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子(需要和左右孩子中最小的交换)
            if(childIndex+1<length&&array[childIndex+1]<array[childIndex]){
                    //定位右孩子
                    childIndex++;
            }
            // 如果父节点小于的任何一个孩子的值,则直接跳出(没有交换的必要了)
            if(temp<=array[childIndex]){
                break;
            }
            无须真正交换,单向赋值即可
            array[parentIndex] = array[childIndex];
            //在基于现在往下找。。
            parentIndex = childIndex;
            childIndex = 2*childIndex+1;
        }
        //最后在将下沉的父节点赋值进去
        array[parentIndex] = temp;
    }
 

 
 从最后一个非叶子节点开始,也就是从节点10开始。
 接下来轮到节点3,
 
然后轮到节点1,
 接下来轮到节点7,
 
 
 经过上述几轮比较和“下沉”操作,最终每一节点都小于它的左、右孩子节点,一个无序的完全二叉树就被构建成了一个最小堆。
总结:
堆的插入操作是单一节点的“上浮”,堆的删除操作是单一节点的“下沉”,这两个操作的平均交换次数都是堆高度的一半,所以时间复杂度是O(logn)。
 至于堆的构建,需要所有非叶子节点依次“下沉”,所以我觉得时间复杂度是O(n)。
二叉堆虽然是一个完全二叉树,但它的存储方式并不是链式存储,而是顺序存储。换句话说,二叉堆的所有节点都存储在数组中。

类似于层序遍历。。。。。
父节点的下标是parent,那么它的左孩子下标就是 2×parent+1;右孩子下标就是2×parent+2。
在父节点和孩子节点做连续交换时,并不一定要
真的交换,只需要先把交换一方的值存入temp变量,做单向覆盖,循环结束后,再
把temp的值存入交换后的最终位置即可。
二叉堆是实现堆排序及优先队列的基础



















