Java學習筆記——排序算法之進階排序(堆排序與分治並歸排序)
阿新 • • 發佈:2017-05-12
進行 技術分享 ring http 沒有 oid 有序 重復 調整
春蠶到死絲方盡,蠟炬成灰淚始幹
——無題
這裏介紹兩個比較難的算法:
1、堆排序
2、分治並歸排序
先說堆。
這裏請大家先自行了解完全二叉樹的數據結構。
堆是完全二叉樹。大頂堆是在堆中,任意雙親值都大於(或等於)其孩子值,就稱其為大頂堆。
堆排序的步驟:
1、把數組想象成一個堆。數組的index+1就是其對應在堆中的序號
2、調堆中各值的順序,得到大頂堆
3、將堆首位值與堆末尾值交換,最大值排序完畢
4、將堆得大小減1,重復步驟2和步驟3,直到堆中只剩下一個元素。排序完畢
上代碼:
1 public class HeapSort { 2 3 publicstatic void heapSort(int[] arr){ 4 //建立完全二叉樹,從最後一個雙親開始調整雙親值,直到根,調整完畢後大頂堆建立完成 5 for (int i = arr.length >> 1; i > 0; i --) { 6 heapAdjust(arr, i, arr.length);//調用堆要從1到length才符合堆的定義 7 } 8 //堆頂和堆低交換,獲取最大值,然後調整大頂堆 9 for (int i = arr.length - 1; i > 0; i--) { 10 arr[i] = arr[i]^arr[0]; 11 arr[0] = arr[i]^arr[0]; 12 arr[i] = arr[i]^arr[0]; 13 heapAdjust(arr, 1, i);//因為堆計數要從1開始,所以size = endIndex + 1 14 } 15 } 16 //調整大頂堆.先找左子,然後和右子比,取值大的,在和雙親自己比,自己比兒子大,break,否則交換.註意:根要從1開始才能找到左子 17 publicstatic void heapAdjust(int[] arr, int parents, int size){ 18 int j;//孩子們的標記是j,索引全部-1 19 int i = parents;//雙親是i,索引全部-1 20 while (i << 1 <= size) { 21 j = i << 1;//左子 22 if (j + 1 <= size) {//有右子 23 if (arr[j - 1] < arr[j + 1 -1]) 24 j ++; 25 } 26 if (arr[i - 1] > arr[j - 1]) 27 break; 28 arr[i-1] = arr[i-1]^arr[j-1]; 29 arr[j-1] = arr[i-1]^arr[j-1]; 30 arr[i-1] = arr[i-1]^arr[j-1]; 31 i = j;//兒子變為父親,這裏不知道是左子還是右子,所以不能直接通過for循環的叠代步驟<<i調整i值,如果是右子的話就錯了(右子<<1+1) 32 } 33 } 34 }
再說分治並歸排序
這裏先要了解什麽是遞歸
1 public class MergingTest { 2 3 public static void main(String[] args) { 4 mSort(0, 3); 5 } 6 private static void mSort(int left, int right) { 7 int m = (left + right)/2; 8 if (left == right) { 9 System.out.println(left); 10 return; 11 } 12 mSort(left, m); 13 mSort(m+1, right); 14 } 15 }
這幾行代碼是並歸算法的核心。運行代碼將輸出0123456789,雖然看上去很簡單,但是如果真能明白,說明你已經完全理解遞歸的思想了,寫出並歸算法也就不在話下了。
為什麽會輸出0123呢?
代碼執行的走向:1→2→4→2→5→2→1→3→6→3→7→3→1→return
能領悟這個東西就好辦了,上代碼:
1 public class MergingSort { 2 3 public static void mergingSort(int[] arr) { 4 int[] temp = new int[arr.length];
//temp是相當於一張牌,通過left,m,right在邏輯上分成兩個數組,進行分治排序,arr是原數組,對數組排序不傳數組怎麽行?! 5 mSort(arr, temp, 0, arr.length-1); 6 } 7 private static void mSort(int[] arr, int[] temp, int left, int right) {
//這裏的分組邏輯沒有使用到temp和arr,而是把它們作為參數傳入merge方法 8 int m = (left+right)/ 2; 9 if (left == right) { 10 return; 11 } 12 mSort(arr, temp, left, m); 13 mSort(arr, temp, m+1, right); 14 merge(arr,temp,left,m,right); 15 } 16 private static void merge(int[]arr, int[] temp, int left, int m, int right) {
//分治排序極其簡單,已知兩個有序數組,要把他們合並成一個有序數組,用什麽方法都不用說,大家一想就知道了。 17 for (int c = 0;c < arr.length;c++){ 18 temp[c] = arr[c]; 19 } 20 for (int p = 0; p < temp.length; p++) { 21 System.out.println("temp["+p+"] = " + temp[p]); 22 } 23 int i = left; 24 int j = m + 1; 25 int k = left; 26 while (i<=m && j<=right) {
//使用分支結構,把每摞牌最小的那個挑出來給arr 27 if (temp[i] < temp[j]) { 28 arr[k++] = temp[i++]; 29 } 30 if (temp[j] < temp[i]) { 31 arr[k++] = temp[j++]; 32 } 33 }
//使用分支結構,把剩下的那摞牌都塞給arr 34 if (i > m) { 35 while (j <= right) { 36 arr[k++] = temp[j++]; 37 } 38 } 39 if (j > right) { 40 while (i <= m) { 41 arr[k++] = temp[i++]; 42 } 43 } 44 } 45 46 }
Java學習筆記——排序算法之進階排序(堆排序與分治並歸排序)