二叉堆的构建,删除节点以及自我调整等基础的操作是实现堆排序的基础(详看上一篇)。

      在最大堆中,如果删除了堆顶元素,然后经过自我调整后,第二大的元素就会被交换上来,称为最大堆的新堆顶。(在堆排序中的删除,并不是真正的删除,而是将其与完全二叉树最后的一个元素交换位置)。例如下图分析:

 

 对于上图,黑色节点为每次最大堆的堆顶与最末尾元素换下来的元素,红色节点为每次换完调整后的最大堆的最大值。

从上可分析排序算法步骤:

  1. 先将无序数组构建成二叉堆,需要从小到大排序,构建最大堆;需要从大到小排序,则构建最小堆。
  2. 循环删除堆顶元素,替换到二叉堆的末尾,然后重新构建堆产生新的堆顶。

具体实现:

 

/**
*将无序数组构建成二叉堆,所需要的时间复杂度是O(n),而每次交换完都需要重新调整构建,需要(n-1)logn,所以时间复杂度是    O(nlogn)。
*
*/
public class StackSort {
    public static void main(String[] args) {
        int[] array = new int[]{1,5,6,3,2,8,7,9};
//        构建堆
        for (int i = (array.length - 2)/2; i >= 0; i--) {
            downAdjust(array,i,array.length);
        }
//交换首节点和末尾节点
        for (int i = array.length - 1; i > 0; i--) {
            int tail = array[i];
            array[i] = array[0];
            array[0] = tail;
            downAdjust(array,0,i);  //重新调整堆
        }
        System.out.println(Arrays.toString(array));
    }

    /**
     * "下沉"节点构建大根堆
     * @param array   待调整数组
     * @param i       根结点
     * @param length  有效的位数
     */
    private static void downAdjust(int[] array, int i, int length) {
        int parentIndex = i;    //父节点下标
        int childIndex = 2*parentIndex+1;   //左孩子节点
        int temp = array[parentIndex];   //临时保存父节点的值
        while (childIndex < length){   //存在左孩子
//            如果存在右孩子,并且右孩子比左孩子大的话,定位到右孩子
            if(childIndex+1 < length && array[childIndex+1] > array[childIndex]){
                childIndex = childIndex+1;
            }
//            如果父节点大于左右孩子中最大的,直接退出
            if(temp > array[childIndex]){
                break;
            }
            //否则
            array[parentIndex] = array[childIndex];
            //每次比较都交换,好理解(相对于最终赋值)
            array[childIndex] = temp;
//            更新结点
            parentIndex = childIndex;  //向下比较,大的上浮
            childIndex = 2*parentIndex+1;
        }
//        最终赋值(不需要每次比较都交换,每次只是向下覆盖,只是最终实现赋值)--->推荐
//        array[parentIndex] = temp;
    }

}

 

posted on 2020-08-02 00:10  CccccDi  阅读(112)  评论(0编辑  收藏  举报