排序总结

面试经验

硅谷某前沿小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. mysql 常用命令,连接数据库,查看建表语句,批量导入数据,批量更新数据,连接查询

    1. 1)MySQL 连接本地数据库,从cmd中进入mysql命令编辑器: root root分别为用户名和密码 mysql -uroot -proot 2)MySQL 连接本地数据库,用户名为“ro ...

  2. Apache 日志设置不记录指定文件类型的方法和日志轮

    Apache日志精准的记录了Web访问的记录,但对于访问量很大的站来说,日志文件过大对于分析和保存很不方便.可以在http.conf(或虚拟主机设置文件httpd-vhosts.conf)中进行设置, ...

  3. VI 基本可视模式

    可视模式让你可以选择文件的一部分内容,以便作比如删除,复制等工作. 进入可视模式 v 用v命令进入可视模式.当光标移动时,就能看到有一些文本被高亮显示了,它们就是被选中的内容. 三种可视模式 v 一个 ...

  4. Redis学习之路(006)- Redis学习手册(Hashes数据类型)

    一.概述: 我们可以将Redis中的Hashes类型看成具有String Key和String Value的map容器.所以该类型非常适合于存储值对象的信息.如Username.Password和Ag ...

  5. ROS学习(十三)—— 编写简单的Service和Client (C++)

    一.编写Service节点 1.节点功能: 我们将创建一个简单的service节点("add_two_ints_server"),该节点将接收到两个整形数字,并返回它们的和. 2. ...

  6. ubuntu(14.04) 网路管理

    网络五元素: MAC地址 IP地址 网络掩码 网关 DNS:将ip地址转换成域名 ping ifconfig route /etc/resolv.conf netstat ip nmap cat /e ...

  7. 如何解决 yum安装出现This system is not registered with RHN

    [root@localhost ~]# yum install libtool Loaded plugins: rhnplugin, security This system is not regis ...

  8. 搭建自己的 github.io 博客

    1.前言 github.io 是基于 Github 的 repo 管理,这意味着咱们对其是有绝对的控制,这个跟放在第三方的平台比,可控性要好太多. 使用 github pages 服务搭建博客的好处有 ...

  9. C# 两个时间相减 返回 对应天时分秒

    "; //string sdsdsdsds = "1"; , '); //不足2位 就补充0 足2位 就不变 DateTime dts1 = DateTime.Now; ...

  10. 还没被玩坏的robobrowser(3)——简单的spider

    背景 做一个简单的spider用来获取python selenium实战教程的一些基本信息.因为python selenium每年滚动开课,所以做这样一个爬虫随时更新最新的开课信息是很有必要的. 预备 ...