堆排序(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; }