算法篇---桶式排序和基数排序

桶式排序不再是一种基于比较的排序方法,它是一种比较巧妙的排序方式,但这种排序方式需要待排序的序列满足以下两个特征:

待排序列所有的值处于一个可枚举的范围之类;

待排序列所在的这个可枚举的范围不应该太大,否则排序开销太大。

排序的具体步骤如下:

(1)对于这个可枚举范围构建一个buckets数组,用于记录“落入”每个桶中元素的个数;

(2)将(1)中得到的buckets数组重新进行计算,按如下公式重新计算:

buckets[i] = buckets[i] +buckets[i-1] (其中1<=i<buckets.length);

桶式排序是一种非常优秀的排序算法,时间效率极高,它只要通过2轮遍历:第1轮遍历待排数据,统计每个待排数据“落入”各桶中的个数,第2轮遍历buckets用于重新计算buckets中元素的值,2轮遍历后就可以得到每个待排数据在有序序列中的位置,然后将各个数据项依次放入指定位置即可。

桶式排序的空间开销较大,它需要两个数组,第1个buckets数组用于记录“落入”各桶中元素的个数,进而保存各元素在有序序列中的位置,第2个数组用于缓存待排数据。

桶式排序是稳定的。

如果待排序数据的范围在0~k之间,那么它的时间复杂度是O(k+n)的

桶式排序算法速度很快,因为它的时间复杂度是O(k+n),而基于交换的排序时间上限是nlg n。

但是它的限制多,比如它只能排整形数组。而且当k较大,而数组长度n较小,即k>>n时,辅助数组C[k+1]的空间消耗较大。

当数组为整形,且k和n接近时, 可以用此方法排序。(有的文章也称这种排序算法为“计数排序”)

代码实现:

public class BucketSortTest {
    public static int count = 0;  

    public static void main(String[] args) {  

        int[] data = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };
        print(data);
        bucketSort(data, 0, 10);
        print(data);  

    }  

    public static void bucketSort(int[] data, int min, int max) {
        // 缓存数组
        int[] tmp = new int[data.length];
        // buckets用于记录待排序元素的信息
        // buckets数组定义了max-min个桶
        int[] buckets = new int[max - min];
        // 计算每个元素在序列出现的次数
        for (int i = 0; i < data.length; i++) {
            buckets[data[i] - min]++;
        }
        // 计算“落入”各桶内的元素在有序序列中的位置
        for (int i = 1; i < max - min; i++) {
            buckets[i] = buckets[i] + buckets[i - 1];
        }
        // 将data中的元素完全复制到tmp数组中
        System.arraycopy(data, 0, tmp, 0, data.length);
        // 根据buckets数组中的信息将待排序列的各元素放入相应位置
        for (int k = data.length - 1; k >= 0; k--) {
            data[--buckets[tmp[k] - min]] = tmp[k];
        }
    }  

    public static void print(int[] data) {
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + "\t");
        }
        System.out.println();
    }  

}

  运行结果:

5    3    6    2    1    9    4    8    7
1    2    3    4    5    6    7    8    9    

基数排序,说白了就是进行多次的桶式排序。

基数排序已经不再是一种常规的排序方式,它更多地像一种排序方法的应用,基数排序必须依赖于另外的排序方法。基数排序的总体思路就是将待排序数据拆分成多个关键字进行排序,也就是说,基数排序的实质是多关键字排序。

多关键字排序的思路是将待排数据里德排序关键字拆分成多个排序关键字;第1个排序关键字,第2个排序关键字,第3个排序关键字......然后,根据子关键字对待排序数据进行排序。

多关键字排序时有两种解决方案:

最高位优先法(MSD)(Most Significant Digit first)

最低位优先法(LSD)(Least Significant Digit first)

例如,对如下数据序列进行排序。

192,221,12,23

可以观察到它的每个数据至多只有3位,因此可以将每个数据拆分成3个关键字:百位(高位)、十位、个位(低位)。

如果按照习惯思维,会先比较百位,百位大的数据大,百位相同的再比较十位,十位大的数据大;最后再比较个位。人得习惯思维是最高位优先方式。

如果按照人得思维方式,计算机实现起来有一定的困难,当开始比较十位时,程序还需要判断它们的百位是否相同--这就认为地增加了难度,计算机通常会选择最低位优先法。

基数排序方法对任一子关键字排序时必须借助于另一种排序方法,而且这种排序方法必须是稳定的。

对于多关键字拆分出来的子关键字,它们一定位于0-9这个可枚举的范围内,这个范围不大,因此用桶式排序效率非常好。

对于多关键字排序来说,程序将待排数据拆分成多个子关键字后,对子关键字排序既可以使用桶式排序,也可以使用任何一种稳定的排序方法。

代码实现:

package 基数排序;

import java.util.Arrays;

public class MultiKeyRadixSortTest {  

    public static void main(String[] args) {
        int[] data = new int[] { 1100, 192, 221, 12, 23 };
        print(data);
        radixSort(data, 10, 4);
        System.out.println("排序后的数组:");
        print(data);
    }  

    public static void radixSort(int[] data, int radix, int d) {
        // 缓存数组
        int[] tmp = new int[data.length];
        // buckets用于记录待排序元素的信息
        // buckets数组定义了max-min个桶
        int[] buckets = new int[radix];  

        for (int i = 0, rate = 1; i < d; i++) {  

            // 重置count数组,开始统计下一个关键字
            Arrays.fill(buckets, 0);
            // 将data中的元素完全复制到tmp数组中
            System.arraycopy(data, 0, tmp, 0, data.length);  

            // 计算每个待排序数据的子关键字
            for (int j = 0; j < data.length; j++) {
                int subKey = (tmp[j] / rate) % radix;
                buckets[subKey]++;
            }  

            for (int j = 1; j < radix; j++) {
                buckets[j] = buckets[j] + buckets[j - 1];
            }  

            // 按子关键字对指定的数据进行排序
            for (int m = data.length - 1; m >= 0; m--) {
                int subKey = (tmp[m] / rate) % radix;
                data[--buckets[subKey]] = tmp[m];
            }
            rate *= radix;
        }  

    }  

    public static void print(int[] data) {
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + "\t");
        }
        System.out.println();
    }  

}

  运行结果:

1100    192    221    12    23
排序后的数组:
12    23    192    221    1100    

转自:http://blog.csdn.net/apei830/article/details/6596104

时间: 2024-10-31 20:44:28

算法篇---桶式排序和基数排序的相关文章

桶式排序和基数排序

之前总结了基于比较模型的常见排序算法,它们中最快的也要消耗O(nlogn)时间.但是我们应该知道的是,在一定条件下以线性时间进行排序依然是可能的.桶式排序和基数排序在合适的条件下就是以线性时间执行的算法. 桶式排序(bucket sort): 思想:如果我们限制需要排序的整数的范围,比如说我们有n个整数,范围从0到m-1,我们可以利用这个信息得到一种快速的排序算法.我们留置一个数组,称之为t,大小为m,并初始化为0.于是t有m个单元(桶),开始时它们都是空的.当i被读入时t[i]加一.在所有的输

常见排序算法之计数排序与基数排序

1.计数排序 顾名思义,是对待排序数组中的数据进行统计,然后根据统计的数据进行排序,例如: 待排序数组: a[] = { 100, 123, 112, 123, 201, 123, 112, 156, 156, 178, 185, 156, 123, 112 } 首先取出数组中最大值与最小值(这个不用说了吧) 最大值:201  最小值:100 因此该数组的数值范围为 201 - 100 + 1 = 102 开辟一个大小为102的数组 count[102] 并将所有元素初始化为0 再次遍历数组,以

算法篇---圈排序Cycle Sort

经典排序算法 - Cycle Sort 所谓的圈的定义,我只能想到用例子来说明,实在不好描述 待排数组[ 6 2 4 1 5 9 ] 排完序后[ 1 2 4 5 6 9 ] 数组索引[ 0 1 2 3 4 5 ] 第一部分 第一步,我们现在来观察待排数组和排完后的结果,以及待排数组的索引,可以发现 排完序后的6应该出现在索引4的位置上,而它现在却在位置0上, 记住这个位置啊,一直找到某个数应该待在位置0上我们的任务就完成了 待排数组[ 6 2 4 1 5 9 ] 排完序后[ 1 2 4 5 6

算法导论--------------计数排序and基数排序

计数排序假设n个输入元素中的每一个都介于0和k之间的整数,k为n个数中最大的元素.当k=O(n)时,计数排序的运行时间为θ(n).计数排序的基本思想是:对n个输入元素中每一个元素x,统计出小于等于x的元素个数,根据x的个数可以确定x在输出数组中的最终位置.此过程需要引入两个辅助存放空间,存放结果的B[1...n],用于确定每个元素个数的数组C[0...k].算法的具体步骤如下: (1)根据输入数组A中元素的值确定k的值,并初始化C[1....k]= 0: (2)遍历输入数组A中的元素,确定每个元

线性排序算法---- 计数排序, 基数排序, 桶排序

排序算法里,除了比较排序算法(堆排序,归并排序,快速排序),还有一类经典的排序算法-------线性时间排序算法.听名字就让人兴奋! 线性时间排序,顾名思义,算法复杂度为线性时间O(n) , 非常快,比快速排序还要快的存在,简直逆天.下面我们来仔细看看三种逆天的线性排序算法, 计数排序,基数排序和桶排序. 1计数排序  counting Sort 计数排序 假设 n个 输入元素中的每一个都是在 0 到 k 区间内的一个整数,当 k= O(N) 时,排序运行的时间为 O(N). 计数排序的基本思想

排序算法(七)非比较排序:计数排序、基数排序、桶排序

前面讲的是比较排序算法,主要有冒泡排序,选择排序,插入排序,归并排序,堆排序,快速排序等. 非比较排序算法:计数排序,基数排序,桶排序.在一定条件下,它们的时间复杂度可以达到O(n). 一,计数排序(Counting Sort) (1)算法简介 计数排序(Counting sort)是一种稳定的排序算法.计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数.然后根据数组C来将A中的元素排到正确的位置.它只能对整数进行排序. (2)算法描述和实现 得到待排序数的范围(在

数据结构与算法之——八大排序算法

附:关于这个主题,网上好的文章已经数不胜数,本篇是整合后的文章. 正文: 一.概述 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 本文所指八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短: 二.排序算法详述 1.

深入浅出数据结构C语言版(22)——排序决策树与桶式排序

在(17)中我们对排序算法进行了简单的分析,并得出了两个结论: 1.只进行相邻元素交换的排序算法时间复杂度为O(N2) 2.要想时间复杂度低于O(N2),算法必须进行远距离的元素交换 而今天,我们将对排序算法进行进一步的分析,这一次的分析将针对"使用比较进行排序"的排序算法,到目前为止我们所讨论过的所有排序算法都在此范畴内.所谓"使用比较进行排序",就是指这个算法实现排序靠的就是让元素互相比较,比如插入排序的元素与前一个元素比较,若反序则交换位置,再比如快速排序小于

算法笔记之排序

最近在看<算法笔记>,如果单从算法来说,这本书真正做到了短小精悍,首先以排序入题,那么我们今天也来说说排序. 排序 将一堆杂乱无章的元素按照某种规则有序排列的过程就叫"排序".排序是一种非常基础的算法,有着广泛的理论和实践基础.对一个排序算法来说,一般从如下3个方面衡量算法的优劣: 时间复杂度:主要是分析关键字的比较次数和记录的移动次数. 空间复杂度:分析排序算法中需要多少辅助内存 稳定性:若两个记录A和B的关键字值相等,但排序后A.B的先后次序保持不变,则称这种算法是稳定