排序总结

面试经验

硅谷某前沿小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 :

 package Algorithms.sort;

 /*********************************************************
*
* 08-722 Data Structures for Application Programmers
* Lab 5 Comparing MergeSort with QuickSort
*
* A simple QuickSort implementation
*
*********************************************************/ import java.util.*; public class QuickSort {
//private static final int SIZE = 100000;
private static final int SIZE = 10000;
private static Random rand = new Random(); public static void main(String args[]) {
int[] array = new int[SIZE]; for (int i = 0; i < SIZE; i++)
//array[i] = rand.nextInt();
array[i] = i; //int[] array = {3, 4, 6, 1, 7, 8, 6}; // reversely ordered
/*
for(int i=0;i<SIZE; i++) array[i] = SIZE - i;
*/ quickSort(array); // to make sure sorting works.
// add "-ea" vm argument
assert isSorted(array); System.out.println(isSorted(array));
//printArray(array);
} public static void printArray(int[] arr) {
System.out.println();
for(int i: arr) {
System.out.println(i + " ");
}
} public static void quickSort(int[] arr) {
recQuickSort(arr, 0, arr.length - 1);
} private static void recQuickSort(int[] arr, int left, int right) {
// Just the input parameter.
if (arr == null || left >= right) {
return;
} // we just set the right node to be pivot.
int pivPosition = partition(arr, left, right, arr[right]); recQuickSort(arr, left, pivPosition - 1);
recQuickSort(arr, pivPosition + 1, right);
} // partition the array and return the new pivot position.
private static int partition(int[] arr, int left, int right, int pivot) {
// set the pivot.
int l = left - 1 ;
int r = right; /*
example:
let 6 to be the pivot. (1) At the beginning:
3 4 6 1 7 8 6
l r (2) After the first while loop:
3 4 6 1 7 8 6
l r (3) swap them, then continue to move i and j:
3 4 1 6 7 8 6
l r (3) swap them, then continue to move i and j:
3 4 1 6 7 8 6
l pivot
r
(4) swap the left and the pivot.
3 4 1 6 7 8 6
l pivot */ while (true) {
// Find the first element which does not fulfill the rule
// It will not move out of range because the right node is pivot.
// 浣跨敤< 寰堥噸瑕侊紝杩欐牱鍙互閬垮厤l璺戝埌pivot鐨勪綅缃紝灏辨槸right鐨勪綅缃�
//while (l < r && arr[++l] <= pivot);
while (arr[++l] < pivot); // Find the first element which does not fulfill the rule
// Don't need to move r to be left of LEFT.
while (r > l && arr[--r] > pivot); // If r <= l, means that all the elements is in the right place.
if (r <= l) {
break;
} // Swap the first two elements that does not fit the rule.
swap(arr, r, l);
} // The l pointer point to the first element which is bigger than the pivot.
// So we can put the pivot just here. Because put a big or equal one in the last will not change the rule that:
// all the smaller one is in the left and the right one is in the right.
swap(arr, l, right); return l;
} // private helper method to swap two values in an array
private static void swap(int[] arr, int dex1, int dex2) {
int tmp = arr[dex1];
arr[dex1] = arr[dex2];
arr[dex2] = tmp;
} /**********************************************************
* Check if array is sorted. A simple debugging tool
**********************************************************/
private static boolean isSorted(int[] array) {
return isSorted(array, 0, array.length - 1);
} private static boolean isSorted(int[] array, int lo, int hi) {
for (int i = lo + 1; i <= hi; i++)
if (array[i] < array[i - 1])
return false;
return true;
} }

https://github.com/yuzhangcmu/LeetCode_algorithm/blob/master/sort/QuickSort.java

MergeSort:

 package Algorithms.sort;

 /********************************************************
*
* 08-722 Data Structures for Application Programmers
* Lecture 14 Advanced Sorting
*
* Naive version of Merge Sort
*
*********************************************************/
import java.util.Arrays; public class MergeSort { private static final int SIZE = 10000; public static int[] mergeSort(int[] data) {
// parameter valid judge.
if (data == null) {
return null;
} // the base case.
int len = data.length;
if (len <= 1) {
return data;
} // divide into two arrays.
// create left half and right half.
int left[] = new int[len/2];
int right[] = new int[len - len/2]; System.arraycopy(data, 0, left, 0, len/2);
System.arraycopy(data, len/2, right, 0, len - len/2); // call itself to sort left half
left = mergeSort(left);
right = mergeSort(right); return merge(left, right);
} // Precondition: two input arrays are sorted and they are not null.
private static int[] merge(int[] left, int[] right) {
int len = left.length + right.length; int[] ret = new int[len]; int leftPoint = 0;
int rightPoint = 0; int cur = 0;
while (leftPoint < left.length && rightPoint < right.length) {
if (left[leftPoint] < right[rightPoint]) {
ret[cur++] = left[leftPoint++];
} else {
ret[cur++] = right[rightPoint++];
}
} if (leftPoint >= left.length) {
while (rightPoint < right.length) {
ret[cur++] = right[rightPoint++];
}
} else {
while (leftPoint < left.length) {
ret[cur++] = left[leftPoint++];
}
} return ret;
} public static void main(String[] args) {
int[] a = new int[SIZE];
for (int i = 0; i < SIZE; i++)
a[i] = (int) (Math.random() * SIZE); //mergeSort(a); int[] test = { 42, 12, 89, 27, 94, 63, 3, 78 };
System.out.println(Arrays.toString(mergeSort(test))); // test merge method
int[] left = { 12, 42, 63, 89 };
int[] right = { 3, 27, 78, 94 };
System.out.println(Arrays.toString(merge(left, right))); // test merge method
int[] left2 = {};
int[] right2 = { 3, 27, 78, 94 };
System.out.println(Arrays.toString(merge(left2, right2)));
} }

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. 从不是空的桶子里把项目再放回原来的序列中。
 package Algorithms.sort;

 /**
* @author yovn
*
*/
public class BucketSorter {
public void sort(int[] keys,int from,int len,int max)
{
int[] count = new int[max];
int[] tmp = new int[len]; // count the keys.
for (int i = 0; i < len; i++) {
count[keys[from + i]]++;
} // calculate the position.
// BUG 1: i go from 1 not 0.
for (int i = 1; i < max; i++) {
count[i] = count[i] + count[i - 1];
} // back the array.
System.arraycopy(keys, from, tmp, 0, len); // Place the objects into the right position.
for (int i = len - 1; i >= 0; i--) {
keys[--count[tmp[i]]] = tmp[i];
}
}
/**
* @param args
*/
public static void main(String[] args) { int[] a={1,4,8,3,2,9,5,0,7,6,9,10,9,13,14,15,11,12,17,16};
BucketSorter sorter=new BucketSorter();
sorter.sort(a,0,a.length,20);//actually is 18, but 20 will also work for(int i=0;i<a.length;i++)
{
System.out.print(a[i]+",");
} } }

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 排序算法的更多相关文章

  1. STL源代码分析——STL算法sort排序算法

    前言 因为在前文的<STL算法剖析>中,源代码剖析许多,不方便学习,也不方便以后复习.这里把这些算法进行归类,对他们单独的源代码剖析进行解说.本文介绍的STL算法中的sort排序算法,SG ...

  2. STL中sort排序算法第三个参数_Compare的实现本质

    关于C++ STL vector 中的sort排序算法有三种自定义实现,它们本质上都是返回bool类型,提供给sort函数作为第三个参数. 重载运算符 全局的比较函数 函数对象 我认为从实现方式看,重 ...

  3. php和c++自带的排序算法

    PHP的 sort() 排序算法与 C++的 sort() 排序算法均为不稳定的排序算法,也就是说,两个值相同的数经过排序后,两者比较过程中还进行了交换位置,后期开发应主要这个问题

  4. 经典排序算法 – 插入排序Insertion sort

    经典排序算法 – 插入排序Insertion sort  插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕. 插入排序方法分直接插入排序和折半插入排序两种, ...

  5. 排序算法总结(二)归并排序【Merge Sort】

    一.归并排序原理(Wikipedia) 归并排序本质是分治思想的应用,并且各层分治递归可以同时进行 1.申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 2.设定两个指针,最初位置 ...

  6. 排序算法总结(一)插入排序【Insertion Sort】

    最近在忙着找工作,以前看的排序算法都忘记了,悲剧啦T  T现在来回顾一下吧. 这边推荐一个算法可视化的网站,非常有用.http://visualgo.net/ 一.插入排序的思想(Wikipedia) ...

  7. 排序算法-冒泡排序(Bubble Sort)

    package com.wangzhu.sort; /** * 冒泡排序算法 * @ClassName: BubbleSort * @Description: TODO * @author wangz ...

  8. 基础排序算法之快速排序(Quick Sort)

    快速排序(Quick Sort)同样是使用了分治法的思想,相比于其他的排序方法,它所用到的空间更少,因为其可以实现原地排序.同时如果随机选取中心枢(pivot),它也是一个随机算法.最重要的是,快速排 ...

  9. 基础排序算法之并归排序(Merge Sort)

    并归排序是学习分治法 (Merge Sort) 的好例子.而且它相对于选择,插入,冒泡排序来说,算法性能有一定提升.我首先会描述要解决的问题,并给出一个并归排序的例子.之后是算法的思路以及给出伪代码. ...

随机推荐

  1. wait3和wait4函数(转)

    wait3和wait4函数除了可以获取子进程状态转变信息外,还可以获得子进程的资源使用信息. pid_t wait3 ( int *status, int option, struct rusage ...

  2. android dialog加载中动画效果

    //显示动画 dialog = new Dialog(context, R.style.loading); dialog.setContentView(R.layout.loadinglayout); ...

  3. JetBrains C++ IDE CLion配置与评测

    等了大半年的JetBrains C++ IDE千呼万唤始出来!上次我猜2014年肯定发布,今天经@wet2_cn同学的提醒,我去官博一看,嘿!有了!赶紧安装试了一把,感觉这是迄今为止用过最好的Cpp ...

  4. 关于 os模块的常用用法

    作为常用模块中的os模块,需要掌握的用法是非常重要的,今天就在这里把它归纳总结总结,以便自己日后的使用 一.os模块 含义:提供程序与操作系统直接操作的各个功能 二.常用的几个用法 os.getcwd ...

  5. 第1章 Python基础-Python介绍&循环语句 练习题&作业

    1.简述编译型与解释型语言的区别,且分别列出你知道的哪些语言属于编译型,哪些属于解释型? 高级语言分为编译型与解释型两种,分别从执行速度.开发效率.跨平台性三个方面说它们的区别. 编译型语言因为执行的 ...

  6. 关于thymeleaf+layout布局的使用方式,spring boot 访问页面(静态页面及jsp页面)

    首先建立相同部分的html,这里我命名为layout.html,放在了`templates/layout'文件夹下,这个路径以后是会用到的,以下是我的layout的代码,比较粗糙. 但是应该会更好的帮 ...

  7. 使用 http 请求方式获取 eureka server的服务信息

    对于一些系统不能接入 eureka server,又需要访问接入eureka server 的服务. 方法一:直接调用服务的地址是一种实现方式,弊端就是地址是写死的,万一服务地址变更则访问不到. 方法 ...

  8. Jacobi并行拆解【补充】

    作者:桂. 时间:2018-04-24  22:04:52 链接:http://www.cnblogs.com/xingshansi/p/8934373.html 前言 本文为Jacobi并行拆解一文 ...

  9. Linux查看磁盘占用率及文件大小

    查看磁盘占用率: 在 df 命令中使用-h选项,以人类易读的格式输出(例如,5K,500M 及 5G) linux中df命令的功能是用来检查linux服务器的文件系统的磁盘空间占用情况.可以利用该命令 ...

  10. nginx 有关防盗链的设置

    http://blog.csdn.net/longjef/article/details/53284108 关于nginx防盗链的方法网上有很多教程,都可以用,但是我发现很多教程并不完整,所做的防盗链 ...