排序总结

面试经验

硅谷某前沿小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. Java Exceptions

    invalid end header( bad central directory size) 异常描述 java.util.zip.ZipException: invalid END header ...

  2. 使用requests库实现多线程下载

    多线程下载主要用到http请求中的header Content-Length:资源长度,用于确认资源的总长度,从而便于规划每个线程的任务量 Range:bytes=beg1-end1;beg2-end ...

  3. Nginx官方版本说明##Mainline &Stable&Legacy versions

    Nginx (Mainline Stable Legacy)区别 Nginx官网提供了三个类型的版本Mainline version:Mainline 是 Nginx 目前主力在做的版本,可以说是开发 ...

  4. IT软件外包行业深入分析:现状、前途、趋势、待遇 什么是软件外包 外包公司是怎么工作的 软件外包公司的面试 软件外包公司需要什么样的人

    目录: [0] - 为什么要对大学生谈软件外包? [1] - 什么是软件外包? [2] - 软件为什么要外包? [3] - 为什么要承接软件外包 [4] - 做软件外包有前途吗? [5] - 外包公司 ...

  5. Windbg找出memory leak的一种笨办法

    以下内容是转自 http://www.cnblogs.com/fbird/p/5889596.html 以前做项目碰到过一个问题,在客户的站点上面发现有严重的内存泄漏.幸运的是我们找到了重现的步骤,一 ...

  6. .Net Core ORM选择之路,哪个才适合你 通用查询类封装之Mongodb篇 Snowflake(雪花算法)的JavaScript实现 【开发记录】如何在B/S项目中使用中国天气的实时天气功能 【开发记录】微信小游戏开发入门——俄罗斯方块

    .Net Core ORM选择之路,哪个才适合你   因为老板的一句话公司项目需要迁移到.Net Core ,但是以前同事用的ORM不支持.Net Core 开发过程也遇到了各种坑,插入条数多了也特别 ...

  7. 如何利用 Visual Studio 自定义项目或工程模板(转载)

    在开发项目的时候,由其是商业性质的大型项目时,往往需要在每个代码文件上都加上一段关于版权.开发人员的信息,并且名称空间上都需要带有公司的标志.这个时候,是选择在开发的时候手动添加还是自动生成呢? 我们 ...

  8. label 赋值 , 隐藏 , 显示

    <label name='by_stages_number' id='by_stages_number'></label> document.getElementById(&q ...

  9. 字符集不同导致的ORA-00972

    使用ssh登录RAC的两个节点,分别执行相同的sql语句,发现其中一个报错ora-00972: 检查后发现,左侧节点的客户端使用的字符集是SecureCRT的默认字符集,右侧使用的是UTF-8

  10. LINUX-vmstat命令讲解

    vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况. 一般vmstat工具的使用是通过两个 ...