artificerpi

Java 与常用排序算法

Comparison

 

 

  • 时间复杂度
  • 空间复杂度

    算法在计算机上所占用的存储空间,包括存储算法本身所占用的存储空间、算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。

    但在评估一个算法的优良性的时候,前两个基本是固定的,只有算法运行是临时占用的存储空间才会随着数据量的增大而有较大影响。因此,一般以程序运行时占用的临时存储大小作为评估指标。

   

 

快速排序 (Divide and Conquer)

 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)

 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)

 

堆排序 From wikipedia  (Transform and Conquer)

注意,建堆过程可能与代码中不太一样,代码中数组已存在,图示中是逐渐插入过程。

基本思想: 

  创建堆(-->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     }

 

posted @ 2016-09-29 10:36  artificerpi  阅读(271)  评论(0编辑  收藏  举报

Copyright ©2017 artificerpi