首页 > 安全资讯 >

算法学习之线性时间排序之基数排序,计数排序及java实现

12-09-06

先介绍一下概念  计数排序和基数排序都是非比较排序,就是不用进行比较就能排序,相对于堆排序,快速排序,插入排序等都是比较排序,比较排序算法的最坏情况下届都要做0(nlgn)次的比较,堆排序和合并排序都是渐...

先介绍一下概念
  计数排序和基数排序都是非比较排序,就是不用进行比较就能排序,相对于堆排序,快速排序,插入排序等都是比较排序,比较排序算法的最坏情况下届都要做0(nlgn)次的比较,堆排序和合并排序都是渐近最有的比较排序算法,线性时间排序的时间复杂度都是O(n)。
  计数排序的基本思想,假设n个输入元素中的每一个都是介于0到k的整数,此处k为某个整数。当k=O(n)时,计数排序运行时间是O(n),计数排序基本思想是对每一个输入元素x,确定出小于x的元素个数,这样可以把x直接放在最终输出的位置上。例如,有15个元素小于x,那么x就放在第18个输出位置。当有元素相同时,放完后小于个数要减1。
  另外需要两个数组,存放最终结果的数组和存放临时存储的数组(即小于x的个数)。
  它是稳定的。具有相同值的元素在输出数组中的相对次序与他们在输入数组的次序相同。

  基数排序的基本思想,基数排序可以说是扩展了的桶式排序,比如当待排序列在一个很大的范围内,比如0到999999内,那么用桶式排序是很浪费空间的。而基数排序把每个排序码拆成由d个排序码,比如任何一个6位数(不满六位前面补0)拆成6个排序码,分别是个位的,十位的,百位的。。。。
  一般有两种方式:
  1) 高位优先(MSD): 从高位到低位依次对序列排序
  2)低位优先(LSD): 从低位到高位依次对序列排序
  计算机一般采用低位优先法(人类一般使用高位优先),但是采用低位优先时要确保排序算法的稳定性。
  基数排序借助桶式排序,每次按第N位排序时,采用桶式排序。对于如何安排每次落入同一个桶中的数据有两种安排方法:
  1)顺序存储:每次使用桶式排序,放入r个桶中,,相同时增加计数。
  2)链式存储:每个桶通过一个静态队列来跟踪。
  举个例子:
  现在有数组:2,10,78,189,354,8,756,390,356。第一次根据各位数将数组划分为10个队列(当然其中的某些队列可能不含有元素)
  0:10,390
  1:
  2:2
  3:
  4:354
  5:
  6:756,356
  7:
  8:78,8
  9:189
  然后收集成序列:
  10,390,2,354,756,356,78,8,189
  在进行第二次分组:
  0:2,8
  1:10
  2:
  3:
  4:
  5:354,756,356
  6:
  7:78
  8:189
  9:390
  第二次收集:
  2,8,10,354,756,356,78,189,390
  第三次分配:
  0:2,8,10,78
  1:189
  2:
  3:354,356,390
  4:
  5:
  6:
  7:756
  8:
  9:
  最后得到序列:2,8,10,78,189,354,356,390,756。完成排序!
  基数排序其实是利用多关键字先达到局部有序,再调整达到全局有序。
  虽然根据常见情况,有b=O(lgn),基数排序运行时间为Θ(n),看上去比快排的平均Θ(nlgn)更好吧。不过,隐含在Θ符号中的常数因子是不同的。对于要处理的n个关键字,尽管基数排序执行的遍数比快排少,但每一遍的时间要长,所以,哪一个排序更好,取决于底层机器的实现特性(比如,快排通常可以比基数更好的利用硬件缓存),同时还取决于输入数据。此外,利用计数排序作为中间稳定排序的基数排序不是原地排序,而很多 Θ(nlgn)的比较排序算法则可以做到原地排序。因此,内存容量宝贵时,像快排这样的原地排序算法也许更可贵。(来自算法导论)
  桶排序:其基本思想:将区间[0,1]划分为n个相同大小的子区间,也叫桶,然后将n个输入分布到桶中。由于输入均匀,所以,一般不会有一个桶有很多数的情况。输出时,先将每个桶进行排序,然后依次输出各桶元素。其伪代码如下:

  代码写的比较详细,不多说,具体看代码,注释都说的比较清楚了。

  具体实现:
  
[java]
package sort; 
 
import java.util.*; 
 
public class RadixSort { 
    /**
     * 基数排序算法思想: 
     * 先找出最大的数有几位数,这样就确定了要进行几次排序, 
     * 然后建立10个数组进行存放第n位数为0,1,2...9的数字,
     * 存放完之后要进行收集10个数组中的数字到原来的数组中,
     * 然后在进行排序 
     * 第i位数为0,1,2...9的计算方法:array[j] % Math.pow(10, i+1) / Math.pow(10,i)
     * 即第一位数直接除以10的余数就行了,第二位数,则除以100得余数,再除以10得商即为第二位数,以此类推。
     * 
     * @param array
     *            要进行基数排序的数组
     */ 
    public void radixSort(int[] array) { 
        int max, time; 
        // 确定最大的数有几位数,确定排序的次数 
        max = array[0]; 
        for (int i = array.length - 1; i > 0; i--) { 
            if (max < array[i]) { 
                max = array[i]; 
            } 
        } 
        time = 0; 
        while (max > 0) { 
            max /= 10; 
            time++; 
        } 
        // 建立10个数组 
        ArrayList<Integer>[] quene = new ArrayList[10]; 
        for (int i = 0; i < 10; i++) { 
            quene[i] = new ArrayList<Integer>(); 
        } 
 
        // 分别进行time次分配和收集数组,根据不同的位数进行分配到数组中,再收集到原来的数组中 
        for (int i = 0; i < time; i++) { 
            for (int j = 0; j < array.length; j++) { 
                int index = (int) (array[j] % (int) Math.pow(10, i + 1) / Math 
                        .pow(10, i)); 
                quene[index].add(array[j]); 
            } 
            // 收集完了之后进行分配 
            int count = 0; 
            for (int k = 0; k < 10; k++) { 
                while (quene[k].size() > 0) { 
                    array[count] = quene[k].remove(0); 
                    count++; 
                } 
            } 
        } 
    } 
 
    /**
     * 计数排序基本思想: 
     * 计算数组中每个元素x中比x小的元素个数, 
     * 然后就可以把x放在相应位置上 
     * 如:有15个数小于x,则x位于第16个位置上。
     * 
     * @param data
     *            待排序数组
     * @param k
     *            排序数组中最大的数
     */ 
    public int[] countingSort(int[] data, int k) { 
        int result[] = new int[data.length]; 
        int[] temp = new int[k + 1]; 
        // 先初始化临时数组,即保存小于元素x的个数的数组 
        for (int i = 0; i < temp.length; i++) { 
            temp[i] = 0; 
        } 
        // temp [i]即包含等于i的元素个数 
        for (int i = 0; i < data.length; i++) { 
            temp[data[i]] = temp[data[i]] + 1; 
        } 
        // temp [i]即包含小于等于i的元素个数 
        for (int i = 1; i < temp.length; i++) { 
            temp[i] = temp[i] + temp[i - 1]; 
        } 
        // 将结果放入result数组,A[j]直接放在最终位置(小于等于A[j]的个数加1的位置) 
        // temp[data[j]]+1上,由于数组下标和位置相差1 
        for (int j = data.length - 1; j >= 0; j--) { 
            int index = temp[data[j]]; 
            result[index - 1] = data[j]; 
            temp[data[j]]--; 
        } 
        return result; 
    } 
 
    /**
     * 得到数组中的最大数,用于计数排序
     * 
     * @param data
     *            待计算数组
     * @return 返回最大数
     */ 
    public int getMax(int[] data) { 
        int max = data[0]; 
        for (int i = data.length - 1; i > 0; i--) { 
            if (max < data[i]) { 
                max = data[i]; 
            } 
        } 
        return max; 
    } 
 
    public static void main(String[] args) { 
        RadixSort redix = new RadixSort(); 
        int[] testData = { 2,10,78,189,354,8,756,390,356 }; 
        // 基数排序测试 
        System.out.println("***基数排序测试***"); 
        redix.radixSort(testData); 
        for (int data : testData) { 
            System.out.print(data + ","); 
        } 
 
        // 计数排序测试 
        System.out.println("\n***计数排序测试***"); 
        int[] testData2 = { 9, 3, 5, 3, 6, 5, 3, 7, 4, 9 }; 
        int max = redix.getMax(testData2); 
        int[] countSort = redix.countingSort(testData2, max); 
        for (int data : countSort) { 
            System.out.print(data + ","); 
        } 
    } 
 

  结果截图如下:

  

相关文章
最新文章
热点推荐