各种排序算法的总结、比较与Java实现
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实现的更多相关文章
- 排序算法入门之快速排序(java实现)
快速排序也是一种分治的排序算法.快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并以将整个数组排序,会需要一个额外的数组:而快速排序的排序方式是当两个子数组都有序时 ...
- 十大经典排序算法详细总结(含JAVA代码实现)
原文出处:http://www.cnblogs.com/guoyaohua/p/8600214.html 0.排序算法说明 0.1 排序的定义 对一序列对象根据某个关键字进行排序. 0.2 术语说明 ...
- JAVA十大经典排序算法最强总结(含JAVA代码实现)
0.排序算法说明 0.1 排序的定义 对一序列对象根据某个关键字进行排序. 0.2 术语说明 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面: 不稳定:如果a原本在b的前面,而a=b,排 ...
- 排序算法之直接插入排序(java实现)
package com.javaTest300; import java.util.Arrays; public class Test041 { public static void main(Str ...
- 排序算法入门之归并排序(java实现)
归并排序是采用分治法的典型应用. 参考<数据结构与算法分析-Java语言描述> 归并排序其实要做两件事: (1)"分解"--将序列每次折半划分. (2)"合并 ...
- 排序算法入门之堆排序(Java实现)
堆排序 在学习了二叉堆(优先队列)以后,我们来看看堆排序.堆排序总的运行时间为O(NlonN). 堆的概念 堆是以数组作为存储结构. 可以看出,它们满足以下规律: 设当前元素在数组中以R[i]表示,那 ...
- 各种排序算法思想复杂度及其java程序实现
一.冒泡排序(BubbleSort)1. 基本思想: 设排序表长为n,从后向前或者从前向后两两比较相邻元素的值,如果两者的相对次序不对(A[i-1] > A[i]),则交换它们, 其结果是将最小 ...
- 排序算法入门之插入排序(java实现)
插入排序思想:相当于插入元素,对于第i个元素,i之前的元素已经是有序的了,这时候将第i个元素依次与前面元素比较,插入合适的位置.
- 常用排序算法的Java实现 - 1
学习编程语言时, 我们会接触到许多排序算法, 这里总结了一下常见的排序算法. 不定期更新. * 其实在Java中存在如Collections.sort()这样的方法来自动为我们排序, 不过学习排序算法 ...
- 数据结构(三) 用java实现七种排序算法。
很多时候,听别人在讨论快速排序,选择排序,冒泡排序等,都觉得很牛逼,心想,卧槽,排序也分那么多种,就觉得别人很牛逼呀,其实不然,当我们自己去了解学习后发现,并没有想象中那么难,今天就一起总结一下各种排 ...
随机推荐
- devstack环境搭建
1. devstack部署 参考Quick Start,推荐使用ubuntu16.04进行安装 1.1 配置ubuntu国内源 修改/etc/apt/sources.list内容为 deb http: ...
- Sersync实时备份服务部署实践
- CUDA9.0+tensorflow-gpu1.8.0+Python2.7服务器环境搭建经验
最近在实验室的服务器上搭建Tensorflow,CUDA是默认的9.0,管理员已经装好了,同时环境变量都已经配好. 直接用Anaconda创建的虚拟环境,使用pip install tensorflo ...
- 常见 SQL语句使用 增删改查
一.常见的增删改查(一).查:1.SELECT 列名称 FROM 表名称,其中列名可以是多个,中间用豆号分开,如SELECT LastName,FirstName FROM Persons: 2.SE ...
- DFS(1)——hdu1518Square
一.题目回顾 题目链接:Square 题意:给你M根木棒,请判断这些木棒能否组成正方形. 二.解题思路 DFS+剪枝 [变量说明] sum:木棒的总长度 ave:形成的正方形的边长 maxlen:最长 ...
- android命令模式IntentService 远程下载文件
服务可用在一下情景: 1,用户离开activity后,仍需要继续工作,例如从网络下载文件,播放音乐. 2,无论activity出现或离开,都需要持续工作,例如网络聊天应用. 3,连接网络服务,正在使用 ...
- setcookie函数
在任何请求的服务器响应都会有个头部,默认情况下,头部发送动作会在第一个输出发生时触发,如echo,<html>.(注:php有个header方法手动发送原生header) 由于setcoo ...
- spring笔记(一)
1. 回顾 Struts与Hibernate可以做什么事? Struts, Mvc中控制层解决方案 可以进行请求数据自动封装.类型转换.文件上传.效验… Hibernate, 持久层的解决方案: 可以 ...
- 解决Git无法同步空文件夹的问题
思路:在每个空文件夹下创建空文件,同步后再删除 package org.zln.module1.demo1; import org.apache.log4j.Logger; import java.i ...
- [lucene系列笔记3]用socket把lucene做成一个web服务
上一篇介绍了用lucene建立索引和搜索,但是那些都只是在本机上运行的,如果希望在服务器上做成web服务该怎么办呢? 一个有效的方法就是用socket通信,这样可以实现后端与前端的独立,也就是不管前端 ...