线性时间排序

 

  各种排序算法总结已经介绍了几种能在O(n*log(n))时间内培训n个数的算法。归并排序和堆排序达到了最坏情况下的上界;快速排序在平均情况下达到该上界。这些算法都有一个有趣的性质:在排序的最终结果中,各元素的次序依赖于它们之间的比较。这类算法为比较算法,还有一类算法是线性时间复杂度的排序算法,有计数排序、基数排序和桶排序,当然,这些算法使用运算而不是比较来确定排序顺序的。

1 计数排序

  计数排序假设n个输入元素中的每一个都是0到n区间的一个整数,其中n是某个整数。计数排序的思想是:对每一个输入元素x,确定小于x的元素个数,利用这里信息可以直接把x放到它在输出数组中的位置了。例如,如果有5个元素小于x,则x最后应该放在第6个输出位置上(也就是下标为5)。当几个元素相同时,这一方案要修改一下,因为不能将相同的元素放到一个输出位置上。

  计数排序是稳定的,即具有相同元素值的元素在数组中的先后位置在排序前后不发生改变。计数排序通常被用作基数排序的一个子过程。以下代码是计数排序的Java实现。

// 排序区间范围是[0, MAX)
private final static int MAX = 100; public void countSort(int[] arr) {
// 临时存放排序的输出
int[] aux = new int[arr.length];
// 提供临时存储空间
int[] count = new int[MAX + 1]; // 1.频率统计
for (int i = 0; i < arr.length; i++) {
count[arr[i] + 1]++;
}
//2.频率转换为索引
for (int i = 1; i < count.length; i++) {
count[i] += count[i - 1];
}
// 3.将元素分类
for (int i = 0; i < arr.length; i++) {
aux[count[arr[i]]++] = arr[i];
}
// 4.回写
for (int i = 0; i < arr.length; i++) {
arr[i] = aux[i];
}
}

2 基数排序

  基数排序(radix sorting)将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。 然后从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

  基数排序的方式可以采用LSD(Least sgnificant digital)或MSD(Most sgnificant digital),低位优先的字符串排序(LSD)依赖于键索引计数法,相当于从右到左对字符串中每一个字符进行键索引排序,最后整个字符串数组也就排好序了。而MSD则相反,由键值的最左边开始。以下代码是基数排序的Java实现。

public void radixSortInternal(String[] arr, int index) {
String[] aux = new String[arr.length];
int[] count = new int[26 + 1]; // 1.频率统计
for (int i = 0; i < arr.length; i++) {
count[arr[i].charAt(index) - 'a' + 1]++;
}
//2.频率转换为索引
for (int i = 1; i < count.length; i++) {
count[i] += count[i - 1];
}
// 3.将元素分类
for (int i = 0; i < arr.length; i++) {
aux[count[arr[i].charAt(index) - 'a']++] = arr[i];
}
// 4.回写
for (int i = 0; i < arr.length; i++) {
arr[i] = aux[i];
}
} public void radixSort(String[] arr) {
for (int i = arr[0].length() - 1; i >= 0; i--) {
radixSortInternal(arr, i);
}
}

3 桶排序

  桶排序假设输入数据服从平均分配,平均情况下代价为O(n)。与计数排序类似,因为对输入数据做了假设,所以其速度也很快。具体来说,计数排序假设输入数据都属于一个小区间内的整数,而桶排序则假设输入数据是由一个随机过程产生,该过程将元素均匀独立分布在[0, 1)(假设的)区间上。桶排序将[0, 1)区间划分为几个相同大小的子区间,或称为桶。因为数据量是随机独立分布在[0, 1)上的,所以一般不会出现一个桶内元素过多情况。为了得出排序结果,对每个桶中元素排序,然后遍历整每个桶就得到了排序后的元素。下图是排序过程图,以下代码是Java实现。

// 排序区间范围是[0, MAX)
private final static int MAX = 100; public void bucketSort(int[] arr) {
// 桶大小为10
TreeSet<Integer>[] bucket = new TreeSet[10];
for (int i = 0; i < 10; i++) {
bucket[i] = new TreeSet();
} for (int i = 0; i < arr.length; i++) {
bucket[arr[i] / 10].add(arr[i]);
} for (int i = 0, k = 0; i < 10; i++) {
for (Integer x : bucket[i]) {
arr[k++] = x;
}
}
}

参考资料:

  1、《算法导论》8章-线性时间排序

  2、luoxn28/algorithm_data_structure

  3、各种排序算法总结

 

几种能在O(n*log(n))时间排序的更多相关文章

  1. MySQL四种类型日志:Error Log、General Query Log、Binary Log、Slow Query Log

    MySQL Server 有四种类型的日志——Error Log.General Query Log.Binary Log 和 Slow Query Log. 第一个是错误日志,记录mysqld的一些 ...

  2. 7.5 Point-in-Time (Incremental) Recovery Using the Binary Log 使用binay log 基于时间点恢复

    7.5 Point-in-Time (Incremental) Recovery Using the Binary Log 使用binay log 基于时间点恢复 7.5.1 Point-in-Tim ...

  3. Nginx修改access.log日志时间格式

    一.修改原因 因为要获取nginx访问信息,作为开发的数据使用,但是nginx的access.log文件中的默认的时间格式是这样的: [02/Nov/2017:20:48:25 +0800] 而要求的 ...

  4. java 23种设计模式及具体例子 收藏有时间慢慢看

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代 码可靠性. 毫无疑问,设计模式 ...

  5. Timewarp 一种生成当中帧技术,异步时间扭曲(Asynchronous Timewarp)

    翻译: https://www.oculus.com/blog/asynchronous-timewarp/    异步时间扭曲(Asynchronous Timewarp 时间扭曲,即调整时长) 关 ...

  6. svn查看日志(show log)显示时间为1970的解决方法

    问题: 在修改文件后show log无法显示日志,上面的时间会自动在2016年和1970年间跳,而且设置不了时间.解决方法:1.编辑svnserve.conf,设置“anon-access=none” ...

  7. MVC web api 返回JSON的几种方式,Newtonsoft.Json序列化日期时间去T的几种方式。

    原文链接:https://www.muhanxue.com/essays/2015/01/8623699.html MVC web api 返回JSON的几种方式 1.在WebApiConfig的Re ...

  8. 五种C语言非数值计算的常用经典排序算法

    摘要:排序是计算机的一种操作方法,其目的是将一组"无序"的记录序列调整为"有序"的记录序列,主要分为内部排序和外部排序. 排序 排序是计算机的一种操作方法,其目 ...

  9. 一种时间复杂度为O(n)的排序方法(转载)

    原文地址:http://my.oschina.net/u/158457/blog/28536 排序的方法很特别,有点类似插入排序的味道 先看下代码 public class Sort { privat ...

随机推荐

  1. Andriod之Activity

    eclipse还原默认的面板设计:Window > Reset Perspective> OK1\ 1.多个Activity之间的关系: 2.Intent的基本作用: 3.在一个Activ ...

  2. Your Job Is Not to Write Code

    I am lucky enough to work with a small team of fantastic engineers who truly care about their custom ...

  3. Ext.net-00 VS配置

    1.configSections 节点配置 <section name="extnet" type="Ext.Net.GlobalConfig" requ ...

  4. 获取图片中的文本--MODI

    http://www.aspsnippets.com/Articles/Read-Extract-Text-from-Image-OCR-in-ASPNet-using-C-and-VBNet.asp ...

  5. Oracle_sqlload导数案例

    文件地址:http://115.com/lb/5lbbut5jc6op 案例中的sql_load导数公用到5个文件,分别是bat.ctl.txt.log.bad 5个文件 bat文件: --用户名/用 ...

  6. 一个简单二叉树的C++实现(一)

    很久没有接触二叉树了,写这个当作练手,接下来会比较详细地实现二叉树的各个功能及应用. /* * BinaryTree.cpp * Author: Qiang Xiao * Time: 2015-07- ...

  7. linux ln 命令(转载)

    ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同不的链接,这个命令最常用的参数是-s,具体用法是:ln –s 源文件 目标文件. 当我们需要在不同的目录,用到相同的 ...

  8. python学习之day9

    队列queue 队列是线程安全的,它保证多线程间的数据交互的一致性. 先进先出队列Queue import queue q = queue.Queue(maxsize=3) #maxsize为队列最大 ...

  9. python网络编程——将IPv4地址转换成不同的格式

    1.将IPv4地址转换为32位二进制格式,用做底层网络函数. import socket from binascii import hexlify def convert_IPv4_address() ...

  10. C++ ofstream和ifstream详细用法

    转载地址:http://soft.chinabyte.com/database/460/11433960.shtml ofstream是从内存到硬盘,ifstream是从硬盘到内存,其实所谓的流缓冲就 ...