1、準備知識api
1.堆數組
堆(英語:heap)是計算機科學中一類特殊的數據結構的統稱。堆一般是一個能夠被看作一棵樹的數組對象。堆老是知足下列性質:數據結構
堆中某個節點的值老是不大於或不小於其父節點的值;code
堆老是一棵徹底二叉樹。對象
將根節點最大的堆叫作最大堆或大根堆,根節點最小的堆叫作最小堆或小根堆。常見的堆有二叉堆、斐波那契堆等。blog
堆是線性數據結構,至關於一維數組,有惟一後繼。排序
堆的定義以下:n個元素的序列{k1,k2,ki,…,kn}當且僅當知足下關係時,稱之爲堆。ast
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4...n/2)class
若將和這次序列對應的一維數組(即以一維數組做此序列的存儲結構)當作是一個徹底二叉樹,則堆的含義代表,徹底二叉樹中全部非終端結點的值均不大於(或不小於)其左、右孩子結點的值。由此,若序列{k1,k2,…,kn}是堆,則堆頂元素(或徹底二叉樹的根)必爲序列中n個元素的最小值(或最大值)。test
2.最大堆
把根節點最大的堆叫最大堆。
2、堆排序的思想
堆排序是將一個數組經過數組下標關係虛構出一個最大堆,這樣這個最大堆的根節點是最大的,而後將這個堆的最後一個節點和根節點交換,這個最大值就到了數組的最後,而後數組的長度減一,減一後的數組在調整順序,使其再次成爲一個最大堆,這是根節點就是第二大的元素,而後將重複上面的步驟,知道所有交換完畢,數組就排好了順序。
3、排序
1.將數組構造爲一個虛擬的最大堆
經過下標來構造這個最大堆。
(1)數組下標和堆元素的對應關係。
經過上面的圖,咱們能夠分析出,一個節點,咱們可使用(n-1)/2來計算它的父節點的座標、使用2*n+1來計算它的左節點的座標、使用2*n+2來計算它的右節點的座標。
(2)構造最大堆
遍歷整個數組,構造一個最大堆,每次插入一個數,而後使堆從新成爲一個最大堆。構造過程以下:
首先將1加入堆,這時堆中只有一個元素,數組如今爲[1,2,3,4,5,6,7]。
將2加入堆中,計算2的父節點(1-1)/2=0,2的父節點是數組下標爲0的元素。這時候2成爲了1的左節點,根節點小於子節點,不知足最大堆的定義,因此調整這個堆,讓根節點最大,因此1,2交換位置,數組如今爲[2,1,3,4,5,6,7]。
將3加入堆中,計算3的父節點(2-1)/2=0,3的父節點是數組下標爲0的元素。這時候3成爲了2的右節點,發現根節點2小於3,調整堆,將根節點2和3交換位置,如今數組爲[3,1,2,4,5,6,7]。
重複上面的步驟,將每個元素加入這個堆,加入一個節點後,對比加入的節點和它的父節點的大小,若是新加入的節點大於它的父節點,則將二者交換,而後在比較交換後的節點和它的父節點的大小,知道使堆從新成爲一個最大堆。
構造過程代碼:
2. 經過堆的結構調整來排序。
經過上面的構造,咱們已將一個數組經過下標關係構形成爲了一個虛擬的最大堆,這時候咱們知道這個最大堆的根節點(也就是數組的第一個元素)如今確定是全部數字中最大的一個,而後根據這個已知關係調整這個堆,來達到排序的目的。
調整過程以下:
將數組的第一個元素和數組的最後一個元素交換位置,這樣最大的那個數就到了數組的最後,而後數組長度減一,將最後一個數排除在外,剩餘的數從新調整順序,從新成爲一個最大堆,這樣根節點就變成了一個次大的元素。
而後再次將根元素和如今的最後一個元素(原數組的倒數第二個元素)交換位置,數組長度在減一,而後從新調整堆使其再次成爲一個最大堆。
重複上面的步驟,直到全部的數字都調整完畢,這時數組就排好了順序。
數組調整過程:
找出當前節點和它的左節點、右節點三者中最大的那個節點,若是最大的節點是它本身則不作任何調整,若是最大的節點是它的左節點或者右節點,則和該節點交換位置,而後將交換後的節點做爲當前節點在重複判斷。
重複上面的步驟,使其從新成爲一個最大堆。
調整過程代碼以下:
4、完整代碼
public class HeapSort { public void heapSort(int[] arr) { for (int i = 0; i < arr.length; i++) { heapInsert(arr, i); } int heapSize = arr.length; while (heapSize > 1) { heapify(arr, --heapSize); } } private void heapInsert(int[] arr,int i) { int last = (i - 1) / 2; //計算父節點 while (arr[i] > arr[last]) { // 比較當前節點和父節點 //調整堆 swap(arr,i,last); //繼續判斷他的上一層節點是否知足最大堆 i = last; last = (i - 1) / 2; } } public void heapify(int[] arr, int heapSize) { swap(arr, 0, heapSize--); int cur = 0; while (2 * cur + 1 <= heapSize) { int left = 2 * cur + 1; int right = 2 * cur + 2; int lastMax = heapSize >= right && arr[left] > arr[right] ? arr[left] > arr[cur] ? left : cur : heapSize >= right ? arr[right] > arr[cur] ? right : cur : arr[left] > arr[cur] ? left : cur; if (lastMax == cur) { break; } int temp = arr[cur]; arr[cur] = arr[lastMax]; arr[lastMax] = temp; cur = lastMax; } } public void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } @Test public void test() { int[] arr = {1,2,3,4,5,6,7}; int[] arr1 = Arrays.copyOf(arr, arr.length); heapSort(arr); Arrays.sort(arr1); Assert.assertArrayEquals(arr, arr1); } }
5、複雜度。
時間複雜度:O(nlogn)
空間複雜度:O(1)