Java 與經常使用排序算法

Comparisonjava

 

 

  • 時間複雜度
  • 空間複雜度

    算法在計算機上所佔用的存儲空間,包括存儲算法自己所佔用的存儲空間、算法的輸入輸出數據所佔用的存儲空間和算法在運行過程當中臨時佔用的存儲空間這三個方面。node

    但在評估一個算法的優良性的時候,前兩個基本是固定的,只有算法運行是臨時佔用的存儲空間纔會隨着數據量的增大而有較大影響。所以,通常以程序運行時佔用的臨時存儲大小做爲評估指標。算法

   

 

快速排序 (Divide and Conquer)api

 1     public void quickSort(int[] arr, int l, int r) {
 2         if (l < r) {
 3             int p = partition(arr, l, r);  // divide and conquer
 4             quickSort(arr, l, p - 1);
 5             quickSort(arr, p + 1, r);
 6         }
 7     }
 8 
 9     // select the first element as pivot
10     // use do while loop
11     public int partition(int[] arr, int l, int r) {
12         int i, j, pivot, tmp;
13         pivot = arr[l];
14         i = l;
15         j = r + 1;
16 
17         do {
18             for (i++; i < r && arr[i] <= pivot; i++) ;// do while loop, less than or equal to pivot
19             for (j--; j > l && arr[j] > pivot; j--) ;// do while loop, greater than pivot
20 
21             // swap arr i,j
22             tmp = arr[i];
23             arr[i] = arr[j];
24             arr[j] = tmp;
25         } while (i < j);
26         if (i > j) {  // cross line, swap back
27             tmp = arr[i];
28             arr[i] = arr[j];
29             arr[j] = tmp;
30         }
31 
32         arr[l] = arr[j];  // place the pivot to appropriate position
33         arr[j] = pivot;
34 
35         return j;
36     }

Best: O(nlogn); Worst: O(n^2)  Average: O(nlogn)   1.38nlogn數組

 

歸併排序 (Divide and Conquer)app

 1    void mergeSort(int[] arr, int l, int r) {
 2         if (l < r) {
 3             int m = l + (r - l) / 2;
 4             mergeSort(arr, l, m);      // divide and conquer
 5             mergeSort(arr, m + 1, r);
 6             merge(arr, l, m, r);
 7         }
 8     }
 9 
10     void merge(int[] arr, int l, int m, int r) {
11         int i, j, k, n1, n2;
12         n1 = m - l + 1;   // size of the left part arr
13         n2 = r - m;        // size of the right part arr
14 
15         int[] L = new int[n1];  // tmp arrays
16         int[] R = new int[n2];
17 
18         // copy elements to the tmp arrays
19         for (i = 0; i < n1; i++) {
20             L[i] = arr[l + i];
21         }
22         for (j = 0; j < n2; j++) {
23             R[j] = arr[m + 1 + j];
24         }
25 
26         i = j = 0;
27         k = l;  // point at the beginning position
28 
29         // Copy smaller elements back to arr (overwrite)
30         while (i < n1 && j < n2) {
31             if (L[i] <= R[j]) {
32                 arr[k++] = L[i++];
33             } else {
34                 arr[k++] = R[j++];
35             }
36         }
37 
38         // copy the remaining elements
39         while (i < n1) {
40             arr[k++] = L[i++];
41         }
42         while (j < n2) {
43             arr[k++] = R[j++];
44         }
45     }

always: O(nlogn)less

 

堆排序 From wikipedia  (Transform and Conquer)ide

注意,建堆過程可能與代碼中不太同樣,代碼中數組已存在,圖示中是逐漸插入過程。函數

基本思想: 工具

  建立堆(-->maxHeapify) --> 反覆的調用del_max()函數獲取最大值

    排序具體過程:  

    1. 創建一個堆H[0..n-1]
    2. 把堆首(最大值)和堆尾互換
    3. 把堆的尺寸縮小1,並調用shift_down(0),目的是把新的數組頂端數據調整到相應位置
    4. 重複步驟2,直到堆的尺寸爲1
  1 public class HeapSort {
  2     private static int[] sort = new int[]{1,0,10,20,3,5,6,4,9,8,12,17,34,11};
  3     public static void main(String[] args) {
  4         buildMaxHeapify(sort);    // build a max heap
  5         heapSort(sort);             // sort the array
  6         print(sort);
  7     }
  8 
  9     private static void buildMaxHeapify(int[] data){
 10         //沒有子節點的才須要建立最大堆,從最後一個的父節點開始
 11         int startIndex = getParentIndex(data.length - 1);
 12         //從尾端開始建立最大堆,每次都是正確的堆
 13         for (int i = startIndex; i >= 0; i--) {
 14             maxHeapify(data, data.length, i);
 15         }
 16     }
 17 
 18     /**
 19      * 建立最大堆
 20      * @param data
 21      * @param heapSize須要建立最大堆的大小,通常在sort的時候用到,由於最多值放在末尾,末尾就再也不納入最大堆了
 22      * @param index當前須要建立最大堆的位置
 23      */
 24     private static void maxHeapify(int[] data, int heapSize, int index){
 25         // 當前點與左右子節點比較
 26         int left = getChildLeftIndex(index);
 27         int right = getChildRightIndex(index);
 28 
 29         int largest = index;
 30         if (left < heapSize && data[index] < data[left]) {
 31             largest = left;
 32         }
 33         if (right < heapSize && data[largest] < data[right]) {
 34             largest = right;
 35         }
 36         //獲得最大值後可能須要交換,若是交換了,其子節點可能就不是最大堆了,須要從新調整
 37         if (largest != index) {
 38             int temp = data[index];
 39             data[index] = data[largest];
 40             data[largest] = temp;
 41             maxHeapify(data, heapSize, largest);    // recursive to the root node
 42         }
 43     }
 44 
 45     /**
 46      * 排序,最大值放在末尾,data雖然是最大堆,在排序後就成了遞增的
 47      * @param data
 48      */
 49     private static void heapSort(int[] data) {
 50         //末尾與頭交換,交換後調整最大堆
 51         for (int i = data.length - 1; i > 0; i--) {     // i--, ignore the last one element for length i
 52             int temp = data[0];
 53             data[0] = data[i];      // put it to the end
 54             data[i] = temp;
 55             maxHeapify(data, i, 0);
 56         }
 57     }
 58 
 59     /**
 60      * 父節點位置
 61      * @param current
 62      * @return
 63      */
 64     private static int getParentIndex(int current){
 65         return (current - 1) >> 1;         // shift one bit to the right, equals to (current -1) / 2
 66     }
 67 
 68     /**
 69      * 左子節點position注意括號,加法優先級更高
 70      * @param current
 71      * @return
 72      */
 73     private static int getChildLeftIndex(int current){
 74         return (current << 1) + 1;
 75     }
 76 
 77     /**
 78      * 右子節點position
 79      * @param current
 80      * @return
 81      */
 82     private static int getChildRightIndex(int current){
 83         return (current << 1) + 2;
 84     }
 85 
 86     private static void print(int[] data){
 87         int pre = -2;
 88         for (int i = 0; i < data.length; i++) {
 89             if (pre < (int)getLog(i+1)) {
 90                 pre = (int)getLog(i+1);
 91                 System.out.println();
 92             }
 93             System.out.print(data[i] + " |");
 94         }
 95     }
 96 
 97     /**
 98      * 以2爲底的對數
 99      * @param param
100      * @return
101      */
102     private static double getLog(double param){
103         return Math.log(param)/Math.log(2);
104     }
105 }

avg: O(nlogn)  <= 2nlogn   在隨機文件上時間測試的結果顯示heapsort比快速排序要慢,可是能夠比得上mergeSort. (來源於算法設計與分析基礎 2rd Edition P228)

 

選擇排序

 1     void selectSort(int[] arr){
 2         int min;
 3         for(int i = 0;i<arr.length-1;i++){
 4             min = i;
 5             for(int j=i;j<arr.length;j++){
 6                 if(arr[min] > arr[j]){         // find the smallest elements
 7                     min = j;
 8                 }
 9             }
10             int tmp = arr[min];
11             arr[min] = arr[i];
12             arr[i] = tmp;
13         }
14     }

Best: O(n^2); Worst: O(n^2) Average: O(n^2)

 

冒泡排序

    void bubbleSort(int[] arr){
        for(int i=0;i<arr.length;i++){
            for(int j = arr.length-1; j > i; j--){
                if(arr[j] < arr[j-1]){    // find the smallest through bubble
                    int tmp = arr[j];
                    arr[j] = arr[j-1];
                    arr[j-1] = tmp;
                }
            }
        }
    }

Best: O(n^2); Worst: O(n^2) Average: O(n^2)

此代碼實現的最佳時間消耗 O(n^2), 若要使最佳狀況時間開銷達到O(n),能夠設置一個sentinel實現:當不發生交換的時候就break退出,完成了排序。

 

插入排序

 1     void insertSort(int[] arr) {
 2         if (arr.length < 2) {
 3             return;
 4         }
 5 
 6         int i, j;
 7         int temp;
 8         for (i = 1; i < arr.length; i++) {
 9             temp = arr[i];
10             for (j = i - 1; j >= 0 && arr[j] > temp; j--) {  // in-place
11                 arr[j + 1] = arr[j];    // shift
12             }
13             arr[j + 1] = temp;
14         }
15     }

Best: O(n); Worst: O(n^2) Average: O(n^2)

 

Java工具類排序 

java.util.Arrays.sort(arr);
  •  The sorting algorithm is a Dual-Pivot Quicksort by Vladimir Yaroslavskiy, Jon Bentley, and Joshua Bloch. 比上面的快速排序更快。

 

計算Java程序運行時間:

1 long startTime = System.nanoTime();
2 //code
3 long endTime = System.nanoTime();
4 System.out.println("Took "+(endTime - startTime) + " ns"); 

 

 Test It:

 1 public static void main(String[] args) {
 2         int arr[] = {1, 3, 1, 8, 4, 8,3,23,2};
 3         Main main = new Main();
 4         long startTime = System.nanoTime();
 5 //        main.quickSort(arr, 0, arr.length - 1);
 6 //        main.selectSort(arr);
 7 //        main.insertSort(arr);
 8 //        main.bubbleSort(arr);
 9 
10 //code
11         long endTime = System.nanoTime();
12         System.out.println("Took "+(endTime - startTime) + " ns");
13         for (int a : arr) {
14             System.out.println(a);
15         }
16     }
相關文章
相關標籤/搜索