堆排序

堆排序的描述

二叉堆

二叉堆是一个数组,它可以被看成一个近似的完全二叉树。树上的每一个结点对应数组中的一个元素。除了最底层外,该树是完全充满的,而且是从左向右填充。 ----摘自《算法导论》第三版

二叉堆分为:最大堆和最小堆
最大堆性质:所有结点的关键值大于等于其孩子节点的关键值(空结点除外)
最小堆性质:所有结点的关键值小于等于其孩子节点的关键值(空结点除外)

冒泡排序的分析

显然,根据最大堆或者最小堆的性质而言,数组的第一个元素(二叉树的根节点元素)就是整个数列中最大或者最小的值。取出根节点值,并且将末尾的元素移至根节点,维护二叉堆性质,继续取根节点值,直到取出全部值,就会得到一个有序数列。对于堆排序的操作时间复杂度是O(nlogn)。

堆排序升序流程:

  1. 输入一个数组
  2. 构建一个最大堆
  3. 取第一个元素,将末尾元素放到第一个位置上
  4. 维护最大堆性质
  5. 重复3和4步骤,直到取出全部元素

如下表展示堆排序的升序排序:

原始数组 41 26 59 53 21 97 31 58
最大堆 97 58 59 53 21 41 31 26
有序结果 21 26 31 41 53 58 59 97

最大堆的二叉树表示:


堆排序_第1张图片
最大堆二叉树表示

堆排序升序排序的代码

private int leftChild(int parent){
    return 2 * parent + 1;
}

private int rightChild(int parent){
    return 2 * parent + 2;
}

//维护最大堆的性质
//局部最大堆的根节点下标是sIndex,最末下标是eIndex。
//换言之,维护的是从sIndex到eIndex的结点构成的最大堆
private void maxHeapify(int[] array, int sIndex, int eIndex){
    int child;
    int tmp;
    for (tmp = array[sIndex]; leftChild(sIndex) <= eIndex; sIndex = child){
        //确定关键值更大的孩子结点
        child = leftChild(sIndex);
        if (rightChild(sIndex) <= eIndex){
            child = array[leftChild(sIndex)] > array[rightChild(sIndex)] ? leftChild(sIndex) : rightChild(sIndex);
        }
        //如果孩子结点的关键值大于父结点,上升;否则最大堆性质满足结束循环
        if (tmp < array[child])
            array[sIndex] = array[child];
        else
            break;
    }
    //初始的根节点关键值放在正确位置上
    array[sIndex] = tmp;
}

private void swapArray(int[] array, int i, int j){
    int tmp = array[i];
    array[i] = array[j];
    array[j] = tmp;
}

public void heapSort(int[] array){
    //构造最大堆,从底层开始依次向上构建
    for (int i = array.length/2; i >= 0; i--){
        maxHeapify(array, i, array.length - 1);
    }
    //排序
    for (int i = array.length - 1; i > 0; i--){
        swapArray(array, 0, i);
        maxHeapify(array, 0, i - 1);
    }
}

你可能感兴趣的:(堆排序)