1 快速排序(QuickSort)

快速排序是一个就地排序,分而治之,大规模递归的算法。从本质上来说,它是归并排序的就地版本。快速排序可以由下面四步组成。

(1) 如果不多于1个数据,直接返回。
(2) 一般选择序列最左边的值作为支点数据。
(3) 将序列分成2部分,一部分都大于支点数据,另外一部分都小于支点数据。
(4) 对两边利用递归排序数列。

快速排序比大部分排序算法都要快。尽管我们可以在某些特殊的情况下写出比快速排序快的算法,但是就通常情况而言,没有比它更快的了。快速排序是递归的,对于内存非常有限的机器来说,它不是一个好的选择。

2 归并排序(MergeSort)

归并排序先分解要排序的序列,从1分成2,2分成4,依次分解,当分解到只有1个一组的时候,就可以排序这些分组,然后依次合并回原来的序列中,这样就可以排序所有数据。合并排序比堆排序稍微快一点,但是需要比堆排序多一倍的内存空间,因为它需要一个额外的数组。

3 堆排序(HeapSort)

堆排序适合于数据量非常大的场合(百万数据)。

堆排序不需要大量的递归或者多维的暂存数组。这对于数据量非常巨大的序列是合适的。比如超过数百万条记录,因为快速排序,归并排序都使用递归来设计算法,在数据量非常大的时候,可能会发生堆栈溢出错误。

堆排序会将所有的数据建成一个堆,最大的数据在堆顶,然后将堆顶数据和序列的最后一个数据交换。接下来再次重建堆,交换数据,依次下去,就可以排序所有的数据。

4 Shell排序(ShellSort)

Shell排序通过将数据分成不同的组,先对每一组进行排序,然后再对所有的元素进行一次插入排序,以减少数据交换和移动的次数。平均效率是O(nlogn)。其中分组的合理性会对算法产生重要的影响。现在多用D.E.Knuth的分组方法。

Shell排序比冒泡排序快5倍,比插入排序大致快2倍。Shell排序比起QuickSort,MergeSort,HeapSort慢很多。但是它相对比较简单,它适合于数据量在5000以下并且速度并不是特别重要的场合。它对于数据量较小的数列重复排序是非常好的。

5 插入排序(InsertSort)

插入排序通过把序列中的值插入一个已经排序好的序列中,直到该序列的结束。插入排序是对冒泡排序的改进。它比冒泡排序快2倍。一般不用在数据大于1000的场合下使用插入排序,或者重复排序超过200数据项的序列。

6 冒泡排序(BubbleSort)

冒泡排序是最慢的排序算法。在实际运用中它是效率最低的算法。它通过一趟又一趟地比较数组中的每一个元素,使较大的数据下沉,较小的数据上升。它是O(n^2)的算法。

7 交换排序(ExchangeSort)和选择排序(SelectSort)

这两种排序方法都是交换方法的排序算法,效率都是 O(n2)。在实际应用中处于和冒泡排序基本相同的地位。它们只是排序算法发展的初级阶段,在实际中使用较少。

8 基数排序(RadixSort)

基数排序和通常的排序算法并不走同样的路线。它是一种比较新颖的算法,但是它只能用于整数的排序,如果我们要把同样的办法运用到浮点数上,我们必须了解浮点数的存储格式,并通过特殊的方式将浮点数映射到整数上,然后再映射回去,这是非常麻烦的事情,因此,它的使用同样也不多。而且,最重要的是,这样算法也需要较多的存储空间。

9 总结

下面是一个总的表格,大致总结了我们常见的所有的排序算法的特点。

排序法  平均时间 最小时间 最大时间 稳定度 额外空间 备注
冒泡排序  O(n2)  O(n)   O(n2)  稳定 O(1) n小时较好
选择排序  O(n2)  O(n2)  O(n2) 不稳定 O(1) n小时较好
插入排序  O(n2)  O(n)  O(n2) 稳定 O(1) 大部分已排序时较好
基数排序 O(logRB)  O(n) O(logRB) 稳定 O(n)

B是真数(0-9),

R是基数(个十百)

Shell排序 O(nlogn)  - O(ns) 1<s<2 不稳定 O(1) s是所选分组
快速排序 O(nlogn)  O(n2) O(n2) 不稳定 O(logn) n大时较好
归并排序 O(nlogn)  O(nlogn) O(nlogn) 稳定 O(n) 要求稳定性时较好
堆排序 O(nlogn)  O(nlogn) O(nlogn) 不稳定 O(1) n大时较好

参考原帖:http://blog.csdn.net/yuzhihui_no1/article/details/44198701

注:原帖中的表格有误,快排空间复杂度为logN,归排的空间复杂度为O(N),本文已纠正。

附上Java实现:

package com.algorithms;

import java.util.Arrays;
import java.util.Collections;
import java.util.Random; /**
* 各种排序的基本解决方案。
*
* @author Shixiang Wan (shixiangwan@gmail.com)
* @version 1.0
* */
public class LearnSort { public static void main(String[] args)
{
int N = 50000;
Integer[] a = new Integer[N];
Random random = new Random();
for (int i=0; i<N; i++)
a[i] = random.nextInt(N); SortFactory sortFactory = new SortFactory(); Collections.shuffle(Arrays.asList(a));
Long start = System.currentTimeMillis();
Arrays.sort(a);
assert sortFactory.isSorted(a);
System.out.println(">> 快排API:" + (System.currentTimeMillis() - start) + "ms"); Collections.shuffle(Arrays.asList(a));
start = System.currentTimeMillis();
sortFactory.sort_quick(a);
assert sortFactory.isSorted(a);
System.out.println(">> 快速排序:" + (System.currentTimeMillis() - start) + "ms"); Collections.shuffle(Arrays.asList(a));
start = System.currentTimeMillis();
sortFactory.sort_heap(a);
assert sortFactory.isSorted(a);
System.out.println(">> 堆排序:" + (System.currentTimeMillis() - start) + "ms"); Collections.shuffle(Arrays.asList(a));
start = System.currentTimeMillis();
sortFactory.sort_merge(a);
assert sortFactory.isSorted(a);
System.out.println(">> 归并排序:" + (System.currentTimeMillis() - start) + "ms"); Collections.shuffle(Arrays.asList(a));
start = System.currentTimeMillis();
sortFactory.sort_selection(a);
assert sortFactory.isSorted(a);
System.out.println(">> 选择排序:" + (System.currentTimeMillis() - start) + "ms"); Collections.shuffle(Arrays.asList(a));
start = System.currentTimeMillis();
sortFactory.sort_insertion(a);
assert sortFactory.isSorted(a);
System.out.println(">> 插入排序:" + (System.currentTimeMillis() - start) + "ms");
}
} class SortFactory {
/*
* 堆排序
* 唯一能够同时最优地利用空间和时间的方法,在嵌入式系统或低成本的移动设备中很流行
* 但是现代系统的许多应用很少使用它,因为它无法利用缓存。数组元素很少和相邻的其他
* 元素进行比较,因此缓存命中的次数要远远高于大多数比较都在相邻元素间进行的算法,
* 如快速排序,归并排序甚至是希尔排序。但用堆实现的优先队列却越来越重要。因为它能在
* 插入操作、删除最大元素操作混合的动态场景中保证对数级别的运行时间。
* */
public void sort_heap(Comparable[] a) {
int N = a.length;
for (int k = N/2; k >= 1; k--) {
sink(a, k, N);
}
while (N > 1) {
exch(a, 0, N-- - 1); // 算法第4版未考虑减1
sink(a, 1, N);
}
} //由上到下的堆有序化(下沉)的实现
private void sink(Comparable[] a, int k, int N) {
while (2*k <= N) {
int j = 2*k;
if (j < N && less(a[j-1], a[j])) j++; // 算法第4版未考虑减1
if (!less(a[k-1], a[j-1])) break; // 算法第4版未考虑减1
exch(a, k-1, j-1); // 算法第4版未考虑减1
k = j;
}
} /*归并排序
* 时间复杂度与快速排序相同
* */
private Comparable[] aux; public void sort_merge(Comparable[] a) {
aux = new Comparable[a.length];
sort(a, 0, a.length - 1);
} private void sort(Comparable[] a, int lo, int hi) {
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a, lo, mid); //将左半边排序
sort(a, mid+1, hi); //将右半边排序
merge(a, lo, mid, hi);
} private void merge(Comparable[] a, int lo, int mid, int hi)
{
// 原地归并,a数组在mid的左右两侧各自已经排好序,这里将左右合并
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++)
aux[k] = a[k];
for (int k = lo; k <= hi; k++)
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (less(aux[j], aux[i]))
a[k] = aux[j++];
else
a[k] = aux[i++];
} /*快速排序*/
public void sort_quick(Comparable[] a)
{
Collections.shuffle(Arrays.asList(a));
sort_quick(a, 0, a.length - 1);
} public void sort_quick(Comparable[] a, int lo, int hi)
{
if (hi <= lo) return;
int j = partition(a, lo, hi);
sort_quick(a, lo, j-1);
sort_quick(a, j+1, hi);
} public int partition(Comparable[] a, int lo, int hi)
{
int i=lo, j=hi+1;
Comparable v = a[lo];
while (true)
{
while (less(a[++i], v)) if (i == hi) break;
while (less(v, a[--j])) if (j == lo) break;
if (i >= j) break;
exch(a, i, j);
}
exch(a, lo, j);
return j;
} /*插入排序
* 插入排序的思想是数组是部分有序的,然后将无序的部分循环插入到已有序的序列中
* 插入排序对随机顺序的序列的时间复杂度也为O(N^2),但是对于基本有序的序列进行排序时间复杂度为O(N)
* 插入排序能保证它排序时i之前的顺序是i之前所有元素的局部排名
* */
public void sort_insertion(Comparable[] a)
{
int N = a.length;
for (int i=1; i<N; i++)
{
/*从第i个元素开始,往右一个元素向左依次比大小,插到合适的最左面*/
for (int j=i; j>0 && less(a[j], a[j-1]); j--)
{
exch(a, j, j-1);
}
}
} /*选择排序
* 冒泡排序是逐一比较,时间复杂度高,还要经常交换数组,时间复杂度为O(N^2)
* 选择排序时间复杂度是O(N^2),与冒泡排序相比减少了数组交换的次数
* 选择排序能保证它排序时i之前的顺序是全局确定排名
* */
public void sort_selection(Comparable[] a)
{
int N = a.length;
for (int i=0; i<N; i++)
{
int min = i;
/*找出从i个后的最小值,并与i索引值交换,以此类推*/
for (int j = i+1; j<N; j++)
if (less(a[j], a[min])) min = j;
exch(a, i, min);
}
} /*基本函数*/
private boolean less(Comparable v, Comparable w)
{
return v.compareTo(w) < 0;
} private void exch(Comparable[] a, int i, int j)
{
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
} public void show(Comparable[] a)
{
for (Comparable ai : a) {
System.out.print(ai + " ");
}
System.out.println();
} public boolean isSorted(Comparable[] a)
{
for (int i=0; i<a.length; i++)
if (less(a[i], a[i-1]))
return false;
return true;
}
}

运行结果:

>> 快排API:31ms
>> 快速排序:16ms
>> 堆排序:31ms
>> 归并排序:47ms
>> 选择排序:5719ms
>> 插入排序:3204ms

各种排序算法的总结、比较与Java实现的更多相关文章

  1. 排序算法入门之快速排序(java实现)

    快速排序也是一种分治的排序算法.快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序,会需要一个额外的数组:而快速排序的排序方式是当两个子数组都有序时 ...

  2. 十大经典排序算法详细总结(含JAVA代码实现)

    原文出处:http://www.cnblogs.com/guoyaohua/p/8600214.html 0.排序算法说明 0.1 排序的定义 对一序列对象根据某个关键字进行排序. 0.2 术语说明 ...

  3. JAVA十大经典排序算法最强总结(含JAVA代码实现)

    0.排序算法说明 0.1 排序的定义 对一序列对象根据某个关键字进行排序. 0.2 术语说明 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面: 不稳定:如果a原本在b的前面,而a=b,排 ...

  4. 排序算法之直接插入排序(java实现)

    package com.javaTest300; import java.util.Arrays; public class Test041 { public static void main(Str ...

  5. 排序算法入门之归并排序(java实现)

    归并排序是采用分治法的典型应用. 参考<数据结构与算法分析-Java语言描述> 归并排序其实要做两件事: (1)"分解"--将序列每次折半划分. (2)"合并 ...

  6. 排序算法入门之堆排序(Java实现)

    堆排序 在学习了二叉堆(优先队列)以后,我们来看看堆排序.堆排序总的运行时间为O(NlonN). 堆的概念 堆是以数组作为存储结构. 可以看出,它们满足以下规律: 设当前元素在数组中以R[i]表示,那 ...

  7. 各种排序算法思想复杂度及其java程序实现

    一.冒泡排序(BubbleSort)1. 基本思想: 设排序表长为n,从后向前或者从前向后两两比较相邻元素的值,如果两者的相对次序不对(A[i-1] > A[i]),则交换它们, 其结果是将最小 ...

  8. 排序算法入门之插入排序(java实现)

    插入排序思想:相当于插入元素,对于第i个元素,i之前的元素已经是有序的了,这时候将第i个元素依次与前面元素比较,插入合适的位置.

  9. 常用排序算法的Java实现 - 1

    学习编程语言时, 我们会接触到许多排序算法, 这里总结了一下常见的排序算法. 不定期更新. * 其实在Java中存在如Collections.sort()这样的方法来自动为我们排序, 不过学习排序算法 ...

  10. 数据结构(三) 用java实现七种排序算法。

    很多时候,听别人在讨论快速排序,选择排序,冒泡排序等,都觉得很牛逼,心想,卧槽,排序也分那么多种,就觉得别人很牛逼呀,其实不然,当我们自己去了解学习后发现,并没有想象中那么难,今天就一起总结一下各种排 ...

随机推荐

  1. 【Linux运维】Centos7上借助ansible搭建LVS+Keepalived

    安装ansible 安装ansible: [root@localhost ~]# /etc/hosts 192.168.19.129 web129.yanglt.com web129 192.168. ...

  2. 使用CodeBlocks编译64位程序(用的编译器仅仅是windows sdk的)

    需求: -CodeBlocks使用nightly版本: -Windows SDK(我使用的是6.0A,即微软针对vista的,因为这个比较小,你也可以选择其他版本但是要有64位编译器.他也适用于xps ...

  3. BZOJ 3569 DZY Loves Chinese II 树上差分+线性基

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3569 Description 神校XJ之学霸兮,Dzy皇考曰JC. 摄提贞于孟陬兮,惟庚寅 ...

  4. 团队项目-第五次Scrum 会议

    时间:10.31 时长:30分钟 地点:教室(主南201) 工作情况 团队成员 已完成任务 待完成任务 解小锐 修复在接受任务时,前端和后端对接中的bug 完成员工信息的简单初始化 陈鑫 完成hire ...

  5. java获得采集网页内容的方法小结

          为了写一个java的采集程序,从网上学习到3种方法可以获取单个网页内容的方法,主要是运用到是java IO流方面的知识,对其不熟悉,因此写个小结. import java.io.Buffe ...

  6. eclipse快捷键(复制自己看)

    1几个最重要的快捷键 代码助手:Ctrl+Space(简体中文操作系统是Alt+/)快速修正:Ctrl+1单词补全:Alt+/打开外部Java文档:Shift+F2 显示搜索对话框:Ctrl+H快速O ...

  7. input设置为readonly后js设置intput的值后台仍然可以接收到

    今天发现一个奇怪现象,一个input属性readonly的值被设置为readonly,然后有前台js给input设置了新值. 虽然前台看不到效果,但是提交到后台后,仍然可以接收到新值,感觉很奇怪. 我 ...

  8. Linux C++线程池实例

    想做一个多线程服务器测试程序,因此参考了github的一些实例,然后自己动手写了类似的代码来加深理解. 目前了解的线程池实现有2种思路: 第一种: 主进程创建一定数量的线程,并将其全部挂起,此时线程状 ...

  9. PS制作圆角透明图片

    方法一:利用“魔术棒橡皮工具”. 1. 点击“圆角矩形工具”,然后选中打开的背景图片,选中即可. 2.然后,按住“ctrl+enter”,所选边框变为虚线框,然后点击“ctrl+shift+i”反选( ...

  10. 我的python计划

    一直想学习一种脚本语言.现在主流的脚本语言,比较先接触的是python 刚开始了解了一下python,感觉挺适合自己的感觉,学习了一段时间,之中感觉,就好象C++一样,把面向对象和面向过程编程结合了起 ...