package sort; import java.util.Arrays; public class HeapSort { public static void main(String[] args) { int[] array = new int[]{2, 1, 4, 3, 6, 5, 8, 7}; // 接下來就是排序的主體邏輯 sort(array); System.out.println(Arrays.toString(array)); } public static void sort(int[] array) { // 按照徹底二叉樹的特色,從最後一個非葉子節點開始,對於整棵樹進行大根堆的調整 // 也就是說,是按照自下而上,每一層都是自右向左來進行調整的 // 注意,這裏元素的索引是從0開始的 // 另外一件須要注意的事情,這裏的建堆,是用堆調整的方式來作的 // 堆調整的邏輯在建堆和後續排序過程當中複用的 for (int i = array.length / 2 - 1; i >= 0; i--) { adjustHeap(array, i, array.length); } // 上述邏輯,建堆結束 // 下面,開始排序邏輯 for (int j = array.length - 1; j > 0; j--) { // 元素交換 // 說是交換,其實質就是把大頂堆的根元素,放到數組的最後;換句話說,就是每一次的堆調整以後,都會有一個元素到達本身的最終位置 swap(array, 0, j); // 元素交換以後,毫無疑問,最後一個元素無需再考慮排序問題了。 // 接下來咱們須要排序的,就是已經去掉了部分元素的堆了,這也是爲何此方法放在循環裏的緣由 // 而這裏,實質上是自上而下,自左向右進行調整的 adjustHeap(array, 0, j); } } /** * 這裏,是整個堆排序最關鍵的地方,正是由於把這個方法抽取出來,才更好理解了堆排序的精髓,會盡量仔細講解 * * @param array * @param i * @param length */ public static void adjustHeap(int[] array, int i, int length) { // 先把當前元素取出來,由於當前元素可能要一直移動 int temp = array[i]; // 能夠參照sort中的調用邏輯,在堆建成,且完成第一次交換以後,實質上i=0;也就是說,是從根所在的最小子樹開始調整的 // 接下來的講解,都是按照i的初始值爲0來說述的 // 這一段很好理解,若是i=0;則k=1;k+1=2 // 實質上,就是根節點和其左右子節點記性比較,讓k指向這個不超過三個節點的子樹中最大的值 // 這裏,必需要說下爲何k值是跳躍性的。 // 首先,舉個例子,若是a[0] > a[1]&&a[0]>a[2],說明0,1,2這棵樹不須要調整,那麼,下一步該到哪一個節點了呢?確定是a[1]所在的子樹了, // 也就是說,是以本節點的左子節點爲根的那棵小的子樹 // 而若是a[0}<a[2]呢,那就調整a[0]和a[2]的位置,而後繼續調整以a[2]爲根節點的那棵子樹,並且確定是從左子樹開始調整的 // 因此,這裏面的用意就在於,自上而下,自左向右一點點調整整棵樹的部分,直到每一顆小子樹都知足大根堆的規律爲止 for (int k = 2 * i + 1; k < length; k = 2 * k + 1) { System.out.println("k==========="+k); // 讓k先指向子節點中最大的節點 if (k + 1 < length && array[k] < array[k + 1]) { k++; } // 若是發現子節點更大,則進行值的交換 if (array[k] > temp) { swap(array, i, k); // 下面就是很是關鍵的一步了 // 若是子節點更換了,那麼,以子節點爲根的子樹會不會受到影響呢? // 因此,循環對子節點所在的樹繼續進行判斷 i = k; // 若是不用交換,那麼,就直接終止循環了 } else { break; } } } /** * 交換元素 * * @param arr * @param a 元素的下標 * @param b 元素的下標 */ public static void swap(int[] arr, int a, int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } }
參考 html
1 圖解排序算法(三)之堆排序 http://www.javashuo.com/article/p-wtyhbkog-dg.htmljava