排序總結
面試經驗
硅谷某前沿小Startup面試時,問到的一個題目就是寫一個快速排序算法。進而面試官問到了各種算法的算法復雜度,進而又問了Merge Sort 與 QuickSort 的優劣。
對排序算法的全面理解,體現了計算機學生的功底。
現在來講Merge Sort 與Quick Sort 是最流行的算法,以下我們來一步一步地分析:
SORT分類
在計算機科學所使用的排序算法通常被分類為:
- 計算的時間複雜度(最差、平均、和最好表現),依據串列(list)的大小(n)。一般而言,好的表現是O(n logn),且壞的表現是O(n2)。對於一個排序理想的表現是O(n)。僅使用一個抽象關鍵比較運算的排序算法總平均上總是至少需要O(n logn)。
- 記憶體使用量(以及其他電腦資源的使用)
- 穩定性:穩定排序算法會讓原本有相等鍵值的紀錄維持相對次序。也就是如果一個排序算法是穩定的,當有兩個相等鍵值的紀錄R和S,且在原本的串列中R出現在S之前,在排序過的串列中R也將會是在S之前。
- 依據排序的方法:插入、交換、選擇、合併等等。
穩定性
當相等的元素是無法分辨的,比如像是整數,穩定性並不是一個問題。然而,假設以下的數對將要以他們的第一個數字來排序。
(4, 1) (3, 1) (3, 7)(5, 6)
在這個狀況下,有可能產生兩種不同的結果,一個是讓相等鍵值的紀錄維持相對的次序,而另外一個則沒有:
(3, 1) (3, 7) (4, 1) (5, 6) (維持次序)(3, 7) (3, 1) (4, 1) (5, 6) (次序被改變)
不穩定排序算法可能會在相等的鍵值中改變紀錄的相對次序,但是穩定排序算法從來不會如此。不穩定排序算法可以被特別地實作為穩定。作這件事情的一個方式是人工擴充鍵值的比較,如此在其他方面相同鍵值的兩個物件間之比較,(比如上面的比較中加入第二個標准:第二個鍵值的大小)就會被決定使用在原先資料次序中的條目,當作一個同分決賽。然而,要記住這種次序通常牽涉到額外的空間負擔。【1】
穩定的排序
- 冒泡排序(bubble sort)— O(n2)
- 插入排序(insertion sort)—O(n2)
- 桶排序(bucket sort)—O(n);需要O(k)額外空間
- 計數排序(countingsort)—O(n+k);需要O(n+k)額外空間
- 歸並排序(merge sort)—O(n logn);需要O(n)額外空間
- 原地歸並排序— O(n2)
- 二叉排序樹排序(binary tree sort)— O(n logn)期望時間; O(n2)最壞時間;需要O(n)額外空間
不穩定的排序
- 選擇排序(selection sort)—O(n2)
- 堆排序(heap sort)—O(n log n)
- 快速排序(quick sort)—O(n log n)期望時間,O(n2)最壞情況;對於大的、亂數串列一般相信是最快的已知排序
平均時間復雜度
平均時間復雜度由高到低為:
- 冒泡排序O(n2)
- 選擇排序O(n2)
- 插入排序O(n2)
- 堆排序O(n log n)
- 歸並排序O(n log n)
- 快速排序O(n log n)
說明:雖然完全逆序的情況下,快速排序會降到選擇排序的速度,不過從概率角度來說(參考信息學理論,和概率學),不對算法做編程上優化時,快速排序的平均速度比堆排序要快一些。
面試官還會問到同樣的nlogn算法,你是會使用快速排序,還是使用Merge Sort.
一般來講,Quick Sort的速度還是要快一點,而且Merge Sort 還會使用額外的空間。另外就是MergeSort是穩定的排序。而快速排序是不穩定的。
QUICK SORT
Quick Sort 的算法復雜度【4】:
Worst case performanceO(n2)Best case performanceO(n log n) (simple partition)
or O(n) (three-way partition and equal keys)Average case performanceO(n log n)Worst case space complexityO(n) auxiliary (naive)
O(log n) auxiliary (Sedgewick 1978)
Quick Sort 的優勢【3】
See Quicksorton wikipedia:
Typically, quicksort is significantly faster in practice thanother Θ(nlogn) algorithms, because its inner loop can beefficiently implemented on most architectures, and in mostreal-world data, it is possible to make design choices whichminimize the probability of requiring quadratic time.
總而言之
1. Quick Sort 更快
2. 而且是就地排序,空間復雜度為O(1),
3. 是遞歸算法,在不希望使用遞歸時,Quick Sort 又不是好的選擇了。
4. Quick Sort並不是穩定的算法。原先的次序會被打亂。
Merge SORT
Merge Sort 的算法復雜度:【5】
ClassSorting algorithmData structureArrayWorst case performanceO(n log n)Best case performance
O(n log n) typical,
O(n) natural variantAverage case performanceO(n log n)Worst case space complexityO(n) auxiliary
Merge Sort Comparison with other sortalgorithms【2】
Although heapsort has the same time bounds as merge sort, itrequires only Θ(1) auxiliary space instead of merge sort'sΘ(n). On typical modern architectures, efficient quicksort implementations generally outperformmergesort for sorting RAM-based arrays.[citationneeded] On the other hand, merge sort is astable sort and is more efficient at handling slow-to-accesssequential media. Merge sort is often the best choice for sorting alinked list: in this situation it is relativelyeasy to implement a merge sort in such a way that it requires onlyΘ(1) extra space, and the slow random-access performance of alinked list makes some other algorithms (such as quicksort) performpoorly, and others (such as heapsort) completely impossible.
As of Perl 5.8, merge sort is its default sorting algorithm(it was quicksort in previous versions of Perl). In Java, the Arrays.sort()methods use merge sort or a tuned quicksort depending on thedatatypes and for implementation efficiency switch to insertion sort when fewer than seven arrayelements are being sorted.[11]Python uses Timsort,another tuned hybrid of merge sort and insertion sort, that hasbecome the standard sort algorithm in Java SE7,[12]on the Android platform,[13]and in GNU Octave.[14]
1. Merge Sort對於連續的數據結構比較有優勢,因為它是順序地Merge。而QuickSort需要Random讀取。所以對於比如磁帶這種媒體,Merge Sort擁有優勢。
2. Merge Sort 可以不使用遞歸來實現。
3. 從Java, Perl對排序算法的選擇,我們可以看出Merge Sort & QuickSort都相當流行。
代碼:
參考CMU久負盛名的 "Data Structures for Application Programmers 08722 "主頁君稍加改進行,去掉了難以理解的++和--等。
GitHub代碼鏈接-
QuickSort :

1 package Algorithms.sort; 2 3 /********************************************************* 4 * 5 * 08-722 Data Structures for Application Programmers 6 * Lab 5 Comparing MergeSort with QuickSort 7 * 8 * A simple QuickSort implementation 9 * 10 *********************************************************/ 11 12 import java.util.*; 13 14 public class QuickSort { 15 //private static final int SIZE = 100000; 16 private static final int SIZE = 10000; 17 private static Random rand = new Random(); 18 19 public static void main(String args[]) { 20 int[] array = new int[SIZE]; 21 22 for (int i = 0; i < SIZE; i++) 23 //array[i] = rand.nextInt(); 24 array[i] = i; 25 26 //int[] array = {3, 4, 6, 1, 7, 8, 6}; 27 28 // reversely ordered 29 /* 30 for(int i=0;i<SIZE; i++) array[i] = SIZE - i; 31 */ 32 33 quickSort(array); 34 35 // to make sure sorting works. 36 // add "-ea" vm argument 37 assert isSorted(array); 38 39 System.out.println(isSorted(array)); 40 //printArray(array); 41 } 42 43 public static void printArray(int[] arr) { 44 System.out.println(); 45 for(int i: arr) { 46 System.out.println(i + " "); 47 } 48 } 49 50 public static void quickSort(int[] arr) { 51 recQuickSort(arr, 0, arr.length - 1); 52 } 53 54 private static void recQuickSort(int[] arr, int left, int right) { 55 // Just the input parameter. 56 if (arr == null || left >= right) { 57 return; 58 } 59 60 // we just set the right node to be pivot. 61 int pivPosition = partition(arr, left, right, arr[right]); 62 63 recQuickSort(arr, left, pivPosition - 1); 64 recQuickSort(arr, pivPosition + 1, right); 65 } 66 67 // partition the array and return the new pivot position. 68 private static int partition(int[] arr, int left, int right, int pivot) { 69 // set the pivot. 70 int l = left - 1 ; 71 int r = right; 72 73 /* 74 example: 75 let 6 to be the pivot. 76 77 (1) At the beginning: 78 3 4 6 1 7 8 6 79 l r 80 81 82 (2) After the first while loop: 83 3 4 6 1 7 8 6 84 l r 85 86 (3) swap them, then continue to move i and j: 87 3 4 1 6 7 8 6 88 l r 89 90 (3) swap them, then continue to move i and j: 91 3 4 1 6 7 8 6 92 l pivot 93 r 94 (4) swap the left and the pivot. 95 3 4 1 6 7 8 6 96 l pivot 97 98 */ 99 100 while (true) { 101 // Find the first element which does not fulfill the rule 102 // It will not move out of range because the right node is pivot. 103 // 浣跨敤< 寰堥噸瑕侊紝榪欐牱鍙互閬垮厤l璺戝埌pivot鐨勪綅緗紝灝辨槸right鐨勪綅緗� 104 //while (l < r && arr[++l] <= pivot); 105 while (arr[++l] < pivot); 106 107 // Find the first element which does not fulfill the rule 108 // Don't need to move r to be left of LEFT. 109 while (r > l && arr[--r] > pivot); 110 111 // If r <= l, means that all the elements is in the right place. 112 if (r <= l) { 113 break; 114 } 115 116 // Swap the first two elements that does not fit the rule. 117 swap(arr, r, l); 118 } 119 120 // The l pointer point to the first element which is bigger than the pivot. 121 // So we can put the pivot just here. Because put a big or equal one in the last will not change the rule that: 122 // all the smaller one is in the left and the right one is in the right. 123 swap(arr, l, right); 124 125 return l; 126 } 127 128 // private helper method to swap two values in an array 129 private static void swap(int[] arr, int dex1, int dex2) { 130 int tmp = arr[dex1]; 131 arr[dex1] = arr[dex2]; 132 arr[dex2] = tmp; 133 } 134 135 /********************************************************** 136 * Check if array is sorted. A simple debugging tool 137 **********************************************************/ 138 private static boolean isSorted(int[] array) { 139 return isSorted(array, 0, array.length - 1); 140 } 141 142 private static boolean isSorted(int[] array, int lo, int hi) { 143 for (int i = lo + 1; i <= hi; i++) 144 if (array[i] < array[i - 1]) 145 return false; 146 return true; 147 } 148 149 }
https://github.com/yuzhangcmu/LeetCode_algorithm/blob/master/sort/QuickSort.java
MergeSort:

1 package Algorithms.sort; 2 3 /******************************************************** 4 * 5 * 08-722 Data Structures for Application Programmers 6 * Lecture 14 Advanced Sorting 7 * 8 * Naive version of Merge Sort 9 * 10 *********************************************************/ 11 import java.util.Arrays; 12 13 public class MergeSort { 14 15 private static final int SIZE = 10000; 16 17 public static int[] mergeSort(int[] data) { 18 // parameter valid judge. 19 if (data == null) { 20 return null; 21 } 22 23 // the base case. 24 int len = data.length; 25 if (len <= 1) { 26 return data; 27 } 28 29 // divide into two arrays. 30 // create left half and right half. 31 int left[] = new int[len/2]; 32 int right[] = new int[len - len/2]; 33 34 System.arraycopy(data, 0, left, 0, len/2); 35 System.arraycopy(data, len/2, right, 0, len - len/2); 36 37 // call itself to sort left half 38 left = mergeSort(left); 39 right = mergeSort(right); 40 41 return merge(left, right); 42 } 43 44 // Precondition: two input arrays are sorted and they are not null. 45 private static int[] merge(int[] left, int[] right) { 46 int len = left.length + right.length; 47 48 int[] ret = new int[len]; 49 50 int leftPoint = 0; 51 int rightPoint = 0; 52 53 int cur = 0; 54 while (leftPoint < left.length && rightPoint < right.length) { 55 if (left[leftPoint] < right[rightPoint]) { 56 ret[cur++] = left[leftPoint++]; 57 } else { 58 ret[cur++] = right[rightPoint++]; 59 } 60 } 61 62 if (leftPoint >= left.length) { 63 while (rightPoint < right.length) { 64 ret[cur++] = right[rightPoint++]; 65 } 66 } else { 67 while (leftPoint < left.length) { 68 ret[cur++] = left[leftPoint++]; 69 } 70 } 71 72 return ret; 73 } 74 75 public static void main(String[] args) { 76 int[] a = new int[SIZE]; 77 for (int i = 0; i < SIZE; i++) 78 a[i] = (int) (Math.random() * SIZE); 79 80 //mergeSort(a); 81 82 int[] test = { 42, 12, 89, 27, 94, 63, 3, 78 }; 83 System.out.println(Arrays.toString(mergeSort(test))); 84 85 // test merge method 86 int[] left = { 12, 42, 63, 89 }; 87 int[] right = { 3, 27, 78, 94 }; 88 System.out.println(Arrays.toString(merge(left, right))); 89 90 // test merge method 91 int[] left2 = {}; 92 int[] right2 = { 3, 27, 78, 94 }; 93 System.out.println(Arrays.toString(merge(left2, right2))); 94 } 95 96 }
https://github.com/yuzhangcmu/LeetCode_algorithm/blob/master/sort/MergeSort.java
這應該是主頁君見過寫得最好的Quick Sort 算法了。歡迎各位大神拍磚指教。
后續還會補充其它排序算法的代碼,敬請指正。
Bucket sort
相關鏈接:
http://www.blogjava.net/javacap/archive/2007/12/14/167618.html
http://zh.wikipedia.org/wiki/%e6%a1%b6%e6%8e%92%e5%ba%8f
桶排序 (Bucket sort)或所謂的箱排序,是一個排序算法,工作的原理是將數組分到有限數量的桶子里。每個桶子再個別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排序)。桶排序是鴿巢排序的一種歸納結果。當要被排序的數組內的數值是均勻分配的時候,桶排序使用線性時間(Θ(n))。但桶排序並不是 比較排序,他不受到 O(n log n) 下限的影響。
桶排序以下列程序進行:
- 設置一個定量的數組當作空桶子。
- 尋訪序列,並且把項目一個一個放到對應的桶子去。
- 對每個不是空的桶子進行排序。
- 從不是空的桶子里把項目再放回原來的序列中。

1 package Algorithms.sort; 2 3 4 /** 5 * @author yovn 6 * 7 */ 8 public class BucketSorter { 9 public void sort(int[] keys,int from,int len,int max) 10 { 11 int[] count = new int[max]; 12 int[] tmp = new int[len]; 13 14 // count the keys. 15 for (int i = 0; i < len; i++) { 16 count[keys[from + i]]++; 17 } 18 19 // calculate the position. 20 // BUG 1: i go from 1 not 0. 21 for (int i = 1; i < max; i++) { 22 count[i] = count[i] + count[i - 1]; 23 } 24 25 // back the array. 26 System.arraycopy(keys, from, tmp, 0, len); 27 28 // Place the objects into the right position. 29 for (int i = len - 1; i >= 0; i--) { 30 keys[--count[tmp[i]]] = tmp[i]; 31 } 32 } 33 /** 34 * @param args 35 */ 36 public static void main(String[] args) { 37 38 int[] a={1,4,8,3,2,9,5,0,7,6,9,10,9,13,14,15,11,12,17,16}; 39 BucketSorter sorter=new BucketSorter(); 40 sorter.sort(a,0,a.length,20);//actually is 18, but 20 will also work 41 42 43 for(int i=0;i<a.length;i++) 44 { 45 System.out.print(a[i]+","); 46 } 47 48 } 49 50 }
GITHUB:
https://github.com/yuzhangcmu/LeetCode_algorithm/blob/master/sort/BucketSorter.java
【1】http://zh.wikipedia.org/wiki/排序算法
【2】http://en.wikipedia.org/wiki/Merge_sort
【3】http://stackoverflow.com/questions/680541/quick-sort-vs-merge-sort
【4】http://en.wikipedia.org/wiki/Quicksort
【5】http://en.wikipedia.org/wiki/Merge_sort