首页 > 代码库 > 总结: Sort 排序算法

总结: Sort 排序算法

排序总结

面试经验

硅谷某前沿小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 rule102             // 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 rule108             // 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 array129     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 tool137      **********************************************************/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 }
View Code

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 =http://www.mamicode.com/= 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 half38         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 method86         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 method91         int[] left2 = {};92         int[] right2 = { 3, 27, 78, 94 };93         System.out.println(Arrays.toString(merge(left2, right2)));94     }95 96 }
View Code

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. 设置一个定量的数组当作空桶子。
  2. 寻访序列,并且把项目一个一个放到对应的桶子去。
  3. 对每个不是空的桶子进行排序。
  4. 从不是空的桶子里把项目再放回原来的序列中。
技术分享
 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 args35      */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 work41         42         43         for(int i=0;i<a.length;i++)44         {45             System.out.print(a[i]+",");46         }47 48     }49 50 }
View Code

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

总结: Sort 排序算法