几种能在O(n*log(n))时间排序

线性时间排序

  各种排序算法总结已经介绍了几种能在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、各种排序算法总结

分类: 数据结构与算法分析

时间: 2024-08-10 23:28:45

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

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

问题: 在修改文件后show log无法显示日志,上面的时间会自动在2016年和1970年间跳,而且设置不了时间.解决方法:1.编辑svnserve.conf,设置“anon-access=none” 2.在authz中添加 [/]* = r 3,清理svn的cache 即可 svn查看日志(show log)显示时间为1970的解决方法,布布扣,bubuko.com

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

翻译: https://www.oculus.com/blog/asynchronous-timewarp/ 异步时间扭曲(Asynchronous Timewarp 时间扭曲,即调整时长)调查 关于作者:迈克尔·安东诺夫,他是Oculus的首席软件工程师,领导SDK团队,在来Oculus之前,他是Scaleform的CTO,在那里他领导硬件加速Flash矢量图形引擎项目,这个引擎被用到上百个视频游戏中,在他的职业生涯里,他主要关注复杂的多线程体系结构,计算机图形学,编程语言设计领域. 导言: 

3种sort:insertion_sort,merge_sort,quick_sort 插入排序 合并排序 快速排序

插入排序,普通排序 一般 前端够用,样本容量小于1000,根本看不出性能问题 function insertion_sort(arr){ var len=arr.length; for(var j=1;j<len;j++){ var key=arr[j]; var i=j-1; while(i>=0&&arr[i]>key){ arr[i+1]=arr[i]; i=i-1; } arr[i+1]=key; } } 合并排序 merge_sort function _mer

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

原文地址:http://my.oschina.net/u/158457/blog/28536 排序的方法很特别,有点类似插入排序的味道 先看下代码 1 public class Sort { 2 private static boolean[] temp = new boolean[10000]; 3 4 /** 5 * @notice 注意参数中的array数组中的每个元素大小不能超过9999,而且不能有重复元素. 6 * 同时也就意味着array数组大小不能超过10000,其中元素大小在0-

RedisTemplate zSet的使用, 根据点赞排序,和创建时间排序2种方式

使用Redis 对问题下的回答按点赞数排序的思路; 1根据问题id查出所有的回答列表; 2吧回答的ids添加到zset1中; key为id,value为赞的数量;(用于点赞排行); //批量添加 Long add(K var1, Set<ZSetOperations.TypedTuple<V>> var2); 2-2吧回答的ids添加到zset2中;key为id,value为createTime.getLong();(用于创建时间排行); 3对回答进行点赞(取消)的时候 更改zse

Mysql 慢查询和慢查询日志分析

众所周知,大访问量的情况下,可添加节点或改变架构可有效的缓解数据库压力,不过一切的原点,都是从单台mysql开始的.下面总结一些使用过或者研究过的经验,从配置以及调节索引的方面入手,对mysql进行一些优化. 第一步应该做的就是排查问题,找出瓶颈,所以,先从日志入手 开启慢查询日志 mysql>show variables like "%slow%"; 查看慢查询配置,没有则在my.cnf中添加,如下 log-slow-queries = /data/mysqldata/slow

Kafka 消息队列系列之分布式消息队列Kafka

介绍 ApacheKafka®是一个分布式流媒体平台.这到底是什么意思呢?我们认为流媒体平台具有三个关键功能:它可以让你发布和订阅记录流.在这方面,它类似于消??息队列或企业消息传递系统.它允许您以容错方式存储记录流.它可以让您在发生记录时处理记录流.什么是卡夫卡好?它被用于两大类的应用程序:构建可在系统或应用程序之间可靠获取数据的实时流数据管道构建实时流应用程序,可以转换或响应数据流要了解卡夫卡如何做这些事情,让我们深入探索卡夫卡的能力.首先几个概念:Kafka作为一个或多个服务器上的集群运行

HDFS:edit log & fsimage

在NameNode的${dfs.namenode.name.dir}/current目录下,有这样几个文件: 在数据库系统中,log是用于记录写操作的日志的,并使用该Log进行备份.恢复数据等工作.有关写的操作的记录的,目前见过了两种:关系型数据库的log,HBase的WALs等等都是这样的写操作的日志. HDFS也采用了类似的机制.在HDFS中,会将第一次的文件操作,看作一个事务.譬如说一个文件的创建.文件内容追加.文件移动等等写操作.从这个角度来看呢,fsimage文件就相当于HDFS 的元

c#实现几种排序方法

插入排序 1.简介 插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法.它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入.插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间. 2.算法描述 一般来说,插入排序都采用in-place在数组上实现.具体算法描述如下:1.从第一个元素开始,该元素可以认为已经被排序2.