计数排序、桶排序和基数排序

计数排序

基本思想

? 计数排序对一定量的整数排序的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进行排序。

  对于一个输入数组中的一个元素i,只要我们知道了这个数组中比i小的元素的个数x,那么我们就可以直接把i放到第(x+1)个位置,即i的索引为x(索引从0开始)。

算法步骤

?1. 找出数组中的最大值和最小值;

2. 统计数组中每个值i的元素出现的次数,将其存入新数组的第i项;

3. 从最小值开始依次计算小于该元素的元素个数x,则该元素在有序数组中的索引值为x

或者:

  1. 找出数组中的最大值和最小值;
  2. 统计数组中每个值i的元素出现的次数,将其存入新数组的第i项;
  3. 将上面的数组根据索引i的值依次在新数组中展开索引i,其结果则为有序数组。

复杂度和稳定性

时间复杂度:O(n+k);n为数组长度,k为最大最小值差

空间复杂度:O(k)

稳定性:稳定(和实现有关)

代码

//不稳定
static void countingSort(int[] nums) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int i : nums) {
            if (i < min) {
                min = i;
            }
            if (i > max) {
                max = i;
            }
        }
        int[] counter = new int[max - min + 1];
        for (int i : nums) {
            counter[i - min]++;
        }
        int p = 0;
        for (int i = 0; i < counter.length; i++){
            for (int j = 0; j < counter[i]; j++){
                nums[p++] = i + min;
            }
        }
    }
//稳定
static void countingSort2(int[] nums) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int i : nums) {
            if (i < min) {
                min = i;
            }
            if (i > max) {
                max = i;
            }
        }
        int[] counter = new int[max - min + 1];
        for (int i : nums) {
            counter[i - min]++;
        }
        for (int i = 1; i < counter.length; i++)
            counter[i] += counter[i - 1];
        int[] output = new int[nums.length];
        for (int i = nums.length - 1; i >= 0; i--){
            int index = counter[nums[i] - min] - 1;
            if (index < 0)continue;
            output[index] = nums[i];
            counter[nums[i] - min]--;
        }
        System.arraycopy(output, 0, nums, 0, nums.length);
    }

桶排序

基本思想

桶排序是计数排序的升级版。它利用了函数的映射关系(比较规则),高效与否的关键就在于这个映射函数的确定(计数排序可以看做函数映射关系为f(x)=x或f(x)=x-min)。

  假设输入由一个随机过程产生,该过程将元素一致地分布在区间[0,1)上。桶排序的思想就是把区间[0,1)划分成n个相同大小的子区间,或称桶,然后将n个输入数分布到各个桶中去。因为输入数均匀分布在[0,1)上,所以一般不会有很多数落在一个桶中的情况。为得到结果,先对各个桶中的数进行排序,然后按次序把各桶中的元素列出来即可。

算法步骤

? 1) 建立一个函数的映射关系f(x)=ax+b等;

  2) 根据映射关系建立多个桶f(x);

  3) 依次将数据放入对应桶中;

  4) 对桶内数据进行排序,可以继续使用桶排序,也可以使用其他排序;

  5) 依次输出各个桶内数据,即为有序序列。

复杂度和稳定性

最佳情况:T(n) = O(n+k);

最差情况:T(n) = O(n2);

平均情况:T(n) = O(n+k);

稳定性:稳定(但若桶内排序不稳定则整体不稳定);

空间复杂度:O(n+k)。

代码

static void bucketSort(int[] nums) {
        //映射规则为:f(x)= x/10-c,其中常量位:c=min/10
        //排序算法为直接插入排序
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int i : nums) {
            if (i < min) {
                min = i;
            }
            if (i > max) {
                max = i;
            }
        }

        List<List<Integer>> buckets = new ArrayList<>();
        for (int i = 0; i < max / 10 - min / 10 + 1; i++) {
            buckets.add(new ArrayList<Integer>());
        }
        for (int i : nums) {
            int index = i / 10 - min / 10;
            buckets.get(index).add(i);
        }
        int p = 0;
        for (List<Integer> i : buckets) {
            if (i.isEmpty()) continue;
            int[] arr = i.stream().mapToInt(Integer::valueOf).toArray();
            insertSort(arr);
            for (int j : arr){
                nums[p++] = j;
            }
        }
    }

基数排序

基本思想

基数排序(Radix Sort)是桶排序的扩展,它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。

具体做法是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。也可以从最高为开始排。

但是需要注意的是对不同位进行排序的时候必须使用稳定的排序。

代码

排序条件:全为整数,且没有负数

static void radixSort(int[] nums){
        int max = Arrays.stream(nums).max().getAsInt();
        for (int exp = 1; max / exp > 0; exp *= 10){
            expSort(nums, exp);
        }
    }

    static void expSort(int[] nums, int exp){
        int[] buckets = new int[10];
        for (int i : nums) {
            buckets[(i / exp) % 10]++;
        }
        for (int i = 1; i < buckets.length; i++)
            buckets[i] += buckets[i - 1];
        int[] output = new int[nums.length];
        for (int i = nums.length - 1; i >= 0; i--){
            int index = buckets[(nums[i] / exp) % 10] - 1;
            if (index < 0)continue;
            output[index] = nums[i];
            buckets[(nums[i] / exp) % 10]--;
        }
        System.arraycopy(output, 0, nums, 0, nums.length);
    }

时间复杂度:O(n * k),n为输入数组长度,k为输入数组位数

空间复杂度:O(n)

稳定性:稳定

原文地址:https://www.cnblogs.com/hao-blogs/p/12635349.html

时间: 2024-11-02 21:49:50

计数排序、桶排序和基数排序的相关文章

【啊哈!算法】最快最简单的排序——桶排序

转自:http://bbs.ahalei.com/thread-4399-1-1.html 最快最简单的排序——桶排序 在我们生活的这个世界中到处都是被排序过的.站队的时候会按照身高排序,考试的名次需要按照分数排序,网上购物的时候会按照价格排序,电子邮箱中的邮件按照时间排序……总之很多东西都需要排序,可以说排序是无处不在.现在我们举个具体的例子来介绍一下排序算法. 首先出场的我们的主人公小哼,上面这个可爱的娃就是啦.期末考试完了老师要将同学们的分数按照从高到低排序.小哼的班上只有5个同学,这5个

计数排序-桶排序-基数排序

计数排序 计数排序的实现主要是数据样本的特殊性(正整数,明确最大边界)和利用列表索引位置来记录值,索引值来统计个数 最后循环索引,根据值(个数)确定添加多少个 import time def cal_time(func): def wrapper(*args, **kwargs): t1 = time.time() result = func(*args, **kwargs) t2 = time.time() print("%s running time: %s secs." % (f

九大排序算法及其实现- 插入.冒泡.选择.归并.快速.堆排序.计数.基数.桶排序

  闲着的时候看到一篇“九大排序算法在总结”,瞬间觉得之前数据结构其实都有学过,但当初大多数都只是老师随口带过,并没有仔细研究一下.遂觉:这是欠下的账,现在该还了.   排序按照空间分类: In-place sort不占用额外内存或占用常数的内存 插入排序.选择排序.冒泡排序.堆排序.快速排序. Out-place sort:归并排序.计数排序.基数排序.桶排序. 或者按照稳定性分类: stable sort:插入排序.冒泡排序.归并排序.计数排序.基数排序.桶排序. unstable sort

基数、希尔、计数、桶排序

一.基数排序 import random from timewrap import * def list_to_buckets(li, iteration):#这个是用来比较每个位置的大小的数字 """ 因为分成10个本来就是有序的所以排出来就是有序的. :param li: 列表 :param iteration: 装桶是第几次迭代 :return: """ buckets = [[] for _ in range(10)] print('bu

世界上最快最简单的排序——桶排序

今天又是无趣的寒假之中的一天,但是,我准备好好开始学习算法了,我是想每天至少学习一种算法,也不知道能不能坚持下来,反正先试试,万一呢?! 世界上有好多东西都是需要排序的 栗子one:给你一串在十以内的数字进行降序排列 最好就是在输入时就进行归类,可以设一个a[11]的数组,放进去,最后再通过循环输出 #include <stdio.h> int main() { int a[11],i,j,t; for(i=0;i<=10;i++) a[i]=0; //初始化为0 for(i=1;i&l

浅识排序----桶排序

排序,说起来每个人都不陌生.在我们生活中也可以看到很多排序的例子,比如你去超市要按到来的时间早晚排队啦,又比如你去买彩票要选号码啦--总之,这个社会的所有事情都有法则,而排序正是我们去实现这些法则的有效途径. 对我们来说,排序,是一堆数据的有序排列,看似十分简单的事,我们如果能够把它做到高效,才算完美. 桶排序 因为是初学,现在我们来简单说一说什么叫做桶排序. 先举个栗子... 比如说我们班有10个同学参加考试,考试的满分是10分,现在全班同学的分数是乱的,老师要求你将成绩从小大到大排列起来,应

算法导论-- 线性时间排序(计数排序、基数排序、桶排序)

线性时间排序 前面介绍的几种排序,都是能够在复杂度nlg(n)时间内排序n个数的算法,这些算法都是通过比较来决定它们的顺序,这类算法叫做比较排序 .下面介绍的几种算法用运算去排序,且它们的复杂度是线性时间. -------------------------------------- 1.计数排序 计数排序采用的方法是:对每个元素x,去确定小于x的元素的个数,从而就可以知道元素x在输出数组中的哪个位置了. 计数排序的一个重要性质是它是稳定的,即对于相同的两个数,排序后,还会保持它们在输入数组中的

计数排序、基数排序与桶排序

一.计数排序 稳定. 当输入的元素是n 个小区间(0到k)内整数时,它的运行时间是 O(n + k),空间复杂度是O(n). const int K = 100; //计数排序:假设输入数据都属于一个小区间内的整数,可用于解决如年龄排序类的问题 //Input:A[0, ..., n-1], 0 <= A[i] < K //Output:B[0, ..., n-1], sorting of A //Aux storage C[0, ..., K) void CountSort(int A[],

线性排序之基数排序,桶排序,计数排序

基数排序 计数排序 桶排序 基数排序,桶排序,计数排序是三种线性排序方法,突破了比较排序的O(nlogn)的限制.但是只适用于特定的情况. 基数排序 以下为维基百科的描述: 基数排序 : 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零.然后,从最低位开始,依次进行一次排序.这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列. 基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digit

冒泡排序,快速排序,归并排序,插入排序,希尔排序,堆排序,计数排序,桶排序,基数排序

选择排序,冒泡排序,快速排序,归并排序,插入排序,希尔排序,计数排序,桶排序,基数排序 以上是一些常用的排序算法. 选择排序 for(int i = 0; i < n; i++) { int minval = a[i]; int minid = i; for (int j = i+1; j < n; j++) { if (a[j] < minval) { minid = j; minval = a[j]; } } swap(a[i], a[minid]); } 最简单的就是选择排序,就是