1. 程式人生 > >Java學習筆記——排序算法之進階排序(堆排序與分治並歸排序)

Java學習筆記——排序算法之進階排序(堆排序與分治並歸排序)

進行 技術分享 ring http 沒有 oid 有序 重復 調整

春蠶到死絲方盡,蠟炬成灰淚始幹

              ——無題

這裏介紹兩個比較難的算法:

1、堆排序

2、分治並歸排序

先說堆。

這裏請大家先自行了解完全二叉樹的數據結構。

堆是完全二叉樹。大頂堆是在堆中,任意雙親值都大於(或等於)其孩子值,就稱其為大頂堆。

堆排序的步驟:

1、把數組想象成一個堆。數組的index+1就是其對應在堆中的序號

2、調堆中各值的順序,得到大頂堆

3、將堆首位值與堆末尾值交換,最大值排序完畢

4、將堆得大小減1,重復步驟2和步驟3,直到堆中只剩下一個元素。排序完畢

上代碼:

 1 public class HeapSort {
 2 
 3     public
static 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 public
static 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學習筆記——排序算法之進階排序(堆排序與分治並歸排序)