java-数组排序--计数排序、桶排序、基数排序

计数排序引入

不难发现不论是冒泡排序还是插入排序,其排序方法都是通过对每一个数进行两两比较进行排序的,这种方法称为比较排序,实际上对每个数的两两比较严重影响了其效率,理论上比较排序时间复杂度的最低下限为nlog(n),即任何比较排序的时间复杂度将不会低于nlog(n),那么有没有方法能不经过数列比较就能使数列排序呢 ,她们的时间复杂度又是多少呢???

计数排序就是一个非比较排序的算法,一如鱼与熊掌不可兼得,她使用了牺牲空间换时间的方法,使的时间复杂度可以达到Ο(n+k)

假设我们有一个数列arr=[3,4,6,4,8,1] 假设max为数列的最大值,此时我们创建一个大小为max+1的辅助数组sortArr,我们以此遍历arr数组,并将数列中的数对应辅助数组sortArr下标的值+1

那么辅助数组中的数,就是其下标在待排序数组中出现的次数,最后我们通过遍历辅助数组sortArr就能获得一个排序好的序列(为什么要创建max+1长度的辅助序列:因为要将待排序数列与辅助序列的下标进行关联,而max应为辅助序列的最后一个下标值,而序列的下标从0开始,有辅助序列的长度应为max+1)

 1     public static void countSort01(int[] arr) {
 2
 3         /* 获取序列的最大值 */
 4         int max=arr[0];
 5         for (int i = 0; i < arr.length; i++) {
 6             if(arr[i]>max) {
 7                 max=arr[i];
 8             }
 9         }
10
11         /* 创建一个长度为max+1的辅助数组 用来存储与辅助数组对应下标的数值 在待排序数列中出现的次数 */
12         int[] sortArr=new int[max+1];
13         for (int i = 0; i < arr.length; i++) {
14             /* 遍历带排序数列 每次将辅助序列对应下标处的数值+1 */
15             sortArr[arr[i]]++;
16         }
17
18         /* 打印辅助序列 与 排序数 */
19         System.out.println(Arrays.toString(sortArr));
20         for (int i = 0; i < sortArr.length; i++) {
21             for (int j = 0; j < sortArr[i]; j++) {
22                 System.out.print(i+" , ");
23             }
24         }
25
26     }

测试一下

    public static void main(String[] args) {

        Sort.countSort01(new int[] {6,10,6,8,6,8,12,15,9,6,7});

    }
结果:

对引入的改进

从上面看,我们已经能对一个数组进行基本的排序了,但是仔细想想,此时打印的排序的数是不稳定的,甚至于这个数与原数列失去的联系,它仅仅只是表示辅助数列的一个下标值,如果我们是对一个对象进行排序,比如学生[姓名,成绩],通过上种方法只能排出所有的成绩,而原来此成绩是谁的却无从得知了,无疑这样的排序不那么合格,此时,我们对代码进行一些改进 这次我们不再存储出现数字的个数,而是存储其次序

 1     public static void countSort02(int[] arr) {
 2
 3         /* 获取待排序数列的最大值 用来创建辅助数组 */
 4         int max = arr[0];
 5         for (int i = 0; i < arr.length; i++) {
 6             if (arr[i] > max) {
 7                 max = arr[i];
 8             }
 9         }
10
11         /* 创建辅助数组 先在辅助数组存储其对应下标在待排序数中出现的次数 (与countSort01同) */
12         int[] sortArr = new int[max + 1];
13         for (int i = 0; i < arr.length; i++) {
14             sortArr[arr[i]]++;
15         }
16         /* 对辅助数组进行改进 将其存储的对应待排序数列的次数 转换为临时排序数列的下标位置 */
17         for (int i = 1; i < sortArr.length; i++) {
18             sortArr[i] += sortArr[i - 1];
19         }
20         System.out.println("辅助序列: "+Arrays.toString(sortArr));
21         /* 创建临时排序数列 */
22         int[] tsortArr = new int[arr.length];
23         for (int i = 0; i < arr.length; i++) {
24             tsortArr[--sortArr[arr[i]]] = arr[i];
25         }
26         System.out.println("排序数列: "+Arrays.toString(tsortArr));
27
28     }

这样我们就能获取一个排序数列了 测试一下

1     public static void main(String[] args) {
2
3         System.out.println("----------引入-------------");
4         Sort.countSort01(new int[] {6,10,6,8,6,8,12,15,9,6,7});
5         System.out.println();
6         System.out.println("----------改进-------------");
7         Sort.countSort02(new int[] {6,10,6,8,6,8,12,15,9,6,7});
8
9     }

计数排序

因为算法创建了两个0-(max+1)的数组进行辅助,如果是象[2,1,4,5,8]这样的数列自然没有问题,但如果是对像[99,97,96,99,100]这样的数列进行排序 就会造成比较大的空间浪费了,所以我们只需要生成max-min+1长的辅助数组就ok啦

直接上代码

 1     public static void countSort(int[] arr) {
 2
 3         /* 获取待排序数列的最大值,与最小值 用来创建辅助数组 */
 4         int min=arr[0],max = arr[0];
 5         for (int i = 0; i < arr.length; i++) {
 6             if(arr[i]>max) {
 7                 max=arr[i];
 8             }
 9             if(arr[i]<min) {
10                 min=arr[i];
11             }
12         }
13
14         /* 创建辅助数组 先在辅助数组存储其对应下标在待排序数中出现的次数 (与countSort01同) */
15         int[] sortArr = new int[max + 1];
16         sortArr=new int[max-min+1];
17         for (int i = 0; i < arr.length; i++) {
18             sortArr[arr[i]-min]++;
19         }
20         /* 对辅助数组进行改进 将其存储的对应待排序数列的次数 转换为临时排序数列的下标位置 */
21         for (int i = 1; i < sortArr.length; i++) {
22             sortArr[i]+=sortArr[i-1];
23         }
24         System.out.println("辅助序列: "+Arrays.toString(sortArr));
25
26         /* 创建临时排序数列 */
27         int[] tsortArr=new int[arr.length] ;
28         for (int i = 0; i < arr.length; i++) {
29             tsortArr[--sortArr[arr[i]-min]]=arr[i];
30         }
31         System.out.println("排序数列: "+Arrays.toString(tsortArr));
32     }

测试:

 1     public static void main(String[] args) {
 2
 3         System.out.println();
 4         System.out.println("----------引入-------------");
 5         Sort.countSort01(new int[] {6,10,6,8,6,8,12,15,9,6,7});
 6         System.out.println();
 7         System.out.println("----------改进-------------");
 8         Sort.countSort02(new int[] {6,10,6,8,6,8,12,15,9,6,7});
 9         System.out.println();
10         System.out.println("----------计数排序-------------");
11         Sort.countSort(new int[] {6,10,6,8,6,8,12,15,9,6,7});
12
13     }

桶排序一

计数排序很快 但是却有一些限制

1.计数排序只能对整数进行排序,

2.当数列之间比较松散,最大最小之差比较大时会浪费比较大的空间浪费

桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))--百度百科

即桶排序创建多个桶区间 将处在同一区间范围的数放在一个桶里,然后再单独对每个桶中的数列进行排序 当然每个桶区间的范围是多大就需要看情况了 当桶的区间特别小 每一个桶只存储一个数 那么其时间复杂度就与计数排序差不多,单无疑需要花费大量的空间 而设置单个桶的区间太大 比如所有序列都落入到一个桶里,那么 其时间复杂度只取决于对单个桶进行排序的算法的复杂度了

 1     /**
 2      * 桶排序 将待排序数列 放入n个桶中 对于每个数列有
 3      * 最大值 max     最小值 min        桶数 n(最后一个桶只存储最大值)        所以(1-(n-1))中每个桶的区间大小应为(max-min)/(n-1)
 4      *               此时有==> 元素i入桶的序列号为:int t=(arr[i]-min)*(n-1)/(max-min)
 5      * @param arr 待排序数列
 6      * @param n  桶数
 7      */
 8     public static void buckSort(double[] arr,int n) {
 9
10         /* 创建n个桶 使用ArrayList作桶 */
11         List<Double>[] bucks=new ArrayList[n];
12         for (int i = 0; i < bucks.length; i++) {
13             bucks[i]=new ArrayList<>();
14         }
15
16         /* 获取数列的最大值最小值 */
17         double max=arr[0],min= arr[0];
18         for (int i = 0; i < arr.length; i++) {
19             if(arr[i]<min) {
20                 min= arr[i];
21             }
22             else if(arr[i]>max) {
23                 max= arr[i];
24             }
25         }
26
27         /* 将元素以此放入桶中 */
28         for (int i = 0; i < arr.length; i++) {
29             /* 计算元素该入哪个桶 */
30             int t=(int) ((arr[i]-min)*(n-1)/(max-min));
31             bucks[t].add(arr[i]);
32         }
33         /* 对每个桶单独排序 */
34         for (int i = 0; i < bucks.length; i++) {
35             Collections.sort(bucks[i]);
36             System.out.println(bucks[i]);
37         }
38
39         /* 将桶中元素 放回原数组 */
40         for (int i = 0,k=0; i < bucks.length; i++) {
41             for (int j = 0; j < bucks[i].size(); j++) {
42                 arr[k++]=bucks[i].get(j);
43             }
44         }
45         System.out.println("排序数列: "+Arrays.toString(arr));
46     }

测试

1
2     public static void main(String[] args) {
3
4         buckSort(new double[] {1.25,3.22,0.56,5.35,1.24,5.22,4.23,4.22,3.56,3.88,4.23},5);
5
6     }

桶排序二

上述桶排序用在一个固定区段的浮点数序列中,还是挺好的,但如果对[0-1)区间的序列进行排序,我们可以对其x10,使其变成一个[0-10)之间的无序数列,并将其按照个位数值,将其放入0-9的桶中

见代码

 1     public static void buckSort(double[] arr) {
 2
 3         /* 创建10个桶 使用ArrayList作桶 */
 4         List<Double>[] bucks=new ArrayList[10];
 5         for (int i = 0; i < bucks.length; i++) {
 6             bucks[i]=new ArrayList<>();
 7         }
 8
 9         /* 遍历待排序数列 ,将其元素x10并放入相应的桶中 */
10         for (int i = 0; i < arr.length; i++) {
11             int t=(int) (arr[i]*10);
12             bucks[t].add(arr[i]);
13         }
14
15         /* 对每个桶单独排序 */
16         for (int i = 0; i < bucks.length; i++) {
17             Collections.sort(bucks[i]);
18             System.out.println("桶序列"+i+": "+bucks[i]);
19         }
20
21         /* 将桶中元素 放回原数组 */
22         for (int i = 0,k=0; i < bucks.length; i++) {
23             for (int j = 0; j < bucks[i].size(); j++) {
24                 arr[k++]=bucks[i].get(j);
25             }
26         }
27         System.out.println("排序数列: "+Arrays.toString(arr));
28
29     }
30     

测试

    public static void main(String[] args) {

        buckSort(new double[] {0.25,0.33,0.11,0.26,0.65,0.84,0.96,0.44});

    }
    

基数排序

基数排序时根据基数不同 将不同的数分配到不同的桶中,(最低位优先)基数排序先对根据数列的个位数 将其放入0-9的二维数组中 然后以此对十位数 百位数等进行相同操作 最后得到一个有序数列,当然最高位优先其思想也是一样

代码

 1     public static void radixSort(int[] arr) {
 2
 3         /* 创建一个10*arr.length的二维数组 */
 4         int[][] duck=new int[10][arr.length];
 5
 6         /* 先获取最大值 */
 7         int max=arr[0];
 8         for (int i = 0; i < arr.length; i++) {
 9             if(arr[i]>max) {
10                 max=(int) (arr[i]+1);
11             }
12         }
13
14         for (int i = 1; max>0 ; i*=10) {
15             /* 记录每个桶的下标 */
16             int[] count=new int[10];
17
18             for (int j = 0; j < arr.length; j++) {
19                 int t=(arr[j]/i)%10;
20                 duck[t][count[t]++]=arr[j];
21             }
22             /* 将桶中的数放回原数组 等待下一位数的排序 */
23             for (int j = 0,c=0; j < 10; j++) {
24                 for (int k = 0; k < count[j]; k++) {
25                     arr[c++]=duck[j][k];
26                 }
27             }
28
29             max/=i;
30         }
31         System.out.println(Arrays.toString(arr));
32
33     }

测试

    public static void main(String[] args) {

        Sort.radixSort(new int[] {6,10,25,80,612,8,12,15,9,6,7});

    }



参考:

桶排序一:

漫画:什么是桶排序?



如果有地方写的错误,或者有什么疑问与建议,欢迎大家提出来 愿与大家一同进步

原文地址:https://www.cnblogs.com/wangbingc/p/10199549.html

时间: 2024-08-13 13:19:42

java-数组排序--计数排序、桶排序、基数排序的相关文章

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

转自: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分,现在全班同学的分数是乱的,老师要求你将成绩从小大到大排列起来,应

【算法】计数排序、桶排序和基数排序详解

01.计数排序.桶排序与基数排序 并不是所有的排序 都是基于比较的,计数排序和基数排序就不是.基于比较排序的排序方法,其复杂度无法突破\(n\log{n}\) 的下限,但是 计数排序 桶排序 和基数排序是分布排序,他们是可以突破这个下限达到O(n)的的复杂度的. 1. 计数排序 概念 计数排序是一种稳定的线性时间排序算法.计数排序使用一个额外的数组C,使用 C[i] 来计算 i 出现的次数.然后根据数C来将原数组A中的元素排到正确的位置. 复杂度 计数排序的最坏时间复杂度.最好时间复杂度.平均时

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

线性时间排序 前面介绍的几种排序,都是能够在复杂度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