堆排序

堆排序的描述
二叉堆

二叉堆是一个数组,它可以被看成一个近似的完全二叉树。树上的每一个结点对应数组中的一个元素。除了最底层外,该树是完全充满的,而且是从左向右填充。 ----摘自《算法导论》第三版
二叉堆分为:最大堆和最小堆
最大堆性质:所有结点的关键值大于等于其孩子节点的关键值(空结点除外)
最小堆性质:所有结点的关键值小于等于其孩子节点的关键值(空结点除外)
冒泡排序的分析
显然,根据最大堆或者最小堆的性质而言,数组的第一个元素(二叉树的根节点元素)就是整个数列中最大或者最小的值。取出根节点值,并且将末尾的元素移至根节点,维护二叉堆性质,继续取根节点值,直到取出全部值,就会得到一个有序数列。对于堆排序的操作时间复杂度是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
最大堆的二叉树表示:

堆排序
文章图片
最大堆二叉树表示 堆排序升序排序的代码
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); } }

    推荐阅读