堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似彻底二叉树的结构,并同时知足堆积的性质:即子结点的键值或索引老是小于(或者大于)它的父节点。堆排序能够说是一种利用堆的概念来排序的选择排序。分为两种方法:html
同时,咱们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子git
该数组从逻辑上讲就是一个堆结构,咱们用简单的公式来描述一下堆的定义就是:github
建立一个最大堆 H[0……n-1];算法
把堆首(最大值)和堆尾互换;api
把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;数组
重复步骤 2,直到堆的尺寸为 1。数据结构
建立最大堆(Build-Max-Heap)的做用是将一个数组改形成一个最大堆,,接受数组和堆大小两个参数,Build-Max-Heap 将自下而上的调用 Max-Heapify 来改造数组,创建最大堆【注意从非叶子节点开始】。由于 Max-Heapify 可以保证下标 i 的结点以后结点都知足最大堆的性质,因此自下而上的调用 Max-Heapify 可以在改造过程当中保持这一性质。post
堆排序(Heap-Sort)是堆排序的接口算法,Heap-Sort先调用Build-Max-Heap将数组改造为最大堆,而后将堆顶和堆底元素交换,以后将底部上升,最后从新调用Max-Heapify保持最大堆性质【由于 Max-Heapify 可以保证下标 i 的结点以后结点都知足最大堆的性质,因此自下而上的调用 Max-Heapify 可以在改造过程当中保持这一性质。】。因为堆顶元素必然是堆中最大的元素,因此一次操做以后,堆中存在的最大元素被分离出堆,重复n-1次以后,数组排列完毕。整个流程以下:性能
堆排序的时间等于建堆和进行堆调整的时间。学习
构建最大堆大概需进行n/2 * 2 = n次比较和n/2次交换,故时间复杂度为O(n)。
堆调整的时间为(n - 1)*O(log2(n))。
堆排序的时间复杂度为O(n*logn)。
在最优、最坏和平均状况下,其时间复杂度为 O(n*logn)。
堆排序为就地排序,所以空间复杂度为O(1)。
堆排序是不稳定的算法,在构建堆过程当中元素的顺序可能会发生变化。
能归位,每一趟排序有一个元素归位。
// 堆排序(HeapSort):移除位在第一个数据的根节点,并作最大堆调整的递归运算
public int[] heapSort(int[] sourceArray) { // 对 arr 进行拷贝,不改变参数内容 int[] arr = Arrays.copyOf(sourceArray, sourceArray.length); int len = arr.length; buildMaxHeap(arr, len); // 首尾元素交换,长度减一,尾部元素最大 for (int i = len - 1; i > 0; i--) { swap(arr, 0, i); len--; heapify(arr, 0, len); } return arr; }
// 建立最大堆(Build Max Heap):将堆中的全部数据从新排序 private void buildMaxHeap(int[] arr, int len) {
// 非叶子节点开始 for (int i = (int) Math.floor(len / 2); i >= 0; i--) { heapify(arr, i, len); } }
// 最大堆调整(Max Heapify):将堆的末端子节点做调整,使得子节点永远小于父节点 private void heapify(int[] arr, int i, int len) { int left = 2 * i + 1; int right = 2 * i + 2; int largest = i; if (left < len && arr[left] > arr[largest]) { largest = left; } if (right < len && arr[right] > arr[largest]) { largest = right; } if (largest != i) { swap(arr, i, largest); heapify(arr, largest, len); } } private void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }