继上篇博文,今天我将先介绍一下什么是计数排序,将计数排序描述清楚后,再进行后续的桶排序方法解决这个问题。

通常情况下,一提到排序,大家第一反应就是比较,其实,今天我要说的这个计数排序,不是基于比较的排序算法。

计数排序的主要思路(精髓):

针对给定的一组整数数据,在其中任意选取一个数X,统计出这组数中不比X大的所有数据的个数n,那么,此时,X的大小次序就应该在这组数的n+1的位置了。

针对这个思想,有几点要注意:

1. 给定的数组A的数据,必须都是整数

2. 给定的数据比较稀疏的情况下,效率(空间)比较差

计数排序的处理逻辑,分为下面几步:

1. 找出输入数组A中的最小值min,以及最大值max,求出数组A的元素最大距离k = max - min + 1;

2. 初始化计数数组C,其数组长度为K;

3. 统计数组A中每个元素A[i]与最小值min的差值作为C数组的下标对应的元素的个数;

4. 计算出C数组中每个元素C[j]及其之前所有元素的和作为当前元素C[j]的新值;

5. 初始化输出数组B,其长度和输入数组A的长度一样;

6. 对数组A进行倒序的方式,将数组A的元素基于其元素的个数排位,进行对输出数组赋值,其中,考虑输入元素中有重复元素的情况;

理论比较的晦涩吧,那么先上代码吧,下面是JAVA代码实现的计数排序:

 /**
* @author "shihuc"
* @date 2017年1月16日
*/
package jishuSort; import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.Scanner; /**
* @author chengsh05
*
*/
class InnerMaxMin {
int max;
int min;
/**
* @return the max
*/
public int getMax() {
return max;
}
/**
* @param max the max to set
*/
public void setMax(int max) {
this.max = max;
}
/**
* @return the min
*/
public int getMin() {
return min;
}
/**
* @param min the min to set
*/
public void setMin(int min) {
this.min = min;
}
}
public class CountSortDemo { /**
* @param args
*/
public static void main(String[] args) {
File file = new File("./src/jishuSort/sampleCount.txt");
Scanner sc = null;
try {
sc = new Scanner(file);
int N = sc.nextInt();
for(int i=; i<N; i++){
int S = sc.nextInt();
int A[] = new int[S];
for(int j=; j<S; j++){
A[j] = sc.nextInt();
}
int B[] = countSortResult(A);
printResult(B);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if(sc != null){
sc.close();
}
}
} /**
* 在时间复杂度为O(n)情况下,计算出最大值与最小值
*
* @param da
* @return
*/
public static InnerMaxMin getMaxMin(int da[]){
InnerMaxMin mm = new InnerMaxMin();
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int i=; i<da.length; i++){
if(da[i] > max){
max = da[i];
}
if(da[i] < min){
min = da[i];
}
} mm.setMax(max);
mm.setMin(min);
return mm;
} /**
* 获取最大值与最小值之间的距离 k
* 计算逻辑步骤1
*
* @param mm
* @return k
*/
public static int getDistance(InnerMaxMin mm){
return mm.getMax() - mm.getMin() + ;
} /**
* 获取转换后的计数数组C,然后将转换后的计数数组C进行计数。 此步骤的好处,是可以减少计数数组的空间开销,掐头和掐尾。
*
*
* @param A 原数组
* @param mm 最大最小值对象
* @return 差值数组
*/
public static int[] getCountArray(int A[], InnerMaxMin mm){
int K = getDistance(mm);
int min = mm.getMin(); /*
* 初始化计数数组C。
* 计算逻辑步骤2
*/
int C[] = new int[K];
Arrays.fill(C, ); /*
* 计算出每个输入数组A中的元素自生的个数,主要考虑的是有重复的情况,所以先单独计算出每个元素自己一共出现了多少次。
* 计算逻辑步骤3
*/
for(int i=; i<A.length; i++){
int n = A[i];
C[n - min]++;
} /*
* 用到了最简单的动态规划思路,统计原始输入数组A中比X元素小的所有元素的个数之和
* 计算逻辑步骤4
*/
for(int j=; j<C.length; j++){
C[j] = C[j] + C[j - ];
} return C;
} /**
* 计数排序的总入口函数
*
* @param A
* @return
*/
public static int[] countSortResult(int A[]){
/*
* 初始化计数数组的输出数组B。
* 计算逻辑步骤5
*/
int B[] = new int[A.length];
InnerMaxMin mm = getMaxMin(A);
int C[] = getCountArray(A, mm);
int min = mm.getMin(); /*
* 将输入元素基于其每个元素对应的计数个数排序操作,映射到相应的输出数组B的位置上。
* (对于输入数组元素中不比X元素大的元素的个数为n,那么X在排序后的输出数组中的位置为n+1)
* 计算逻辑步骤6
*/
for(int i = A.length - ; i>=; i--){
B[C[A[i] - min] - ] = A[i];
C[A[i] - min]--;
}
return B;
} /**
* 显示最终排序后的结果
*
* @param B
*/
public static void printResult(int B[]){
for(int i=; i<B.length; i++){
System.out.print(B[i] + " ");
}
System.out.println();
} }

下面也附带上我的测试数据:


也附带上测试后的结果:


这个算法,是稳定的算法,性能往往比常规的比较排序要好,但是使用场景也是受限的,即参与排序的元素必须是整数,且最好不要是稀疏数组。另外,计数排序的空间消耗,相比比较排序要高。 理解计数排序的核心思想中的计算的精髓,那么这个算法就理解了。结合代码,进行理解,还是不那么难的。

排序问题思考(要求时间和空间复杂度尽可能的低)【Part 2】的更多相关文章

  1. 栈(stack)、递归(八皇后问题)、排序算法分类,时间和空间复杂度简介

    一.栈的介绍: 1)栈的英文为(stack)2)栈是一个先入后出(FILO-First In Last Out)的有序列表.3)栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的 ...

  2. 原生select默认显示为空胡fish覅神农大丰今年圣诞节奋笔疾书发撒可交付你说的尽快发那段时间南方大厦尽可能放你的所发生的你富家大室耐腐蚀的看法呢尽快发你上课积啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊撒啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊分你束带结发你看

    下拉框默认为空: <select> <option value="" class="blank"></option> < ...

  3. CDQ分治学习思考

    先挂上个大佬讲解,sunyutian1998学长给我推荐的mlystdcall大佬的[教程]简易CDQ分治教程&学习笔记 还有个B站小姐姐讲解的概念https://www.bilibili.c ...

  4. leetcode -- 二进制

    leetcode -- 二进制 在学习编程语言的运算符时,大部分语言都会有与,或等二进制运算符,我在初期学习这些运算符的时候,并没有重点留意这些运算符,并且在后续的业务代码中也没有频繁的使用过,直到后 ...

  5. MySQL应用优化

    MySQL应用优化 目录 MySQL应用优化 1.数据库连接池 2.减少对MySQL的访问 3.负载均衡 4.MySQL查询缓存优化 5.MySQL如何使用缓存 6.MySQL内存管理以及优化 原则 ...

  6. LoadRunner 思考时间与事务响应时间的区别与关系

    LoadRunner 思考时间与事务响应时间的区别与关系   思考时间lr_think_time 就是一个事务要开始时思考的时间;比如 你要点击一个 登录按钮 我们都要点击这个按钮要先思考下 就是人为 ...

  7. LoadRunner ---思考时间设置

    用户访问某个网站或软件,一般不会不停地做个各种操作,例如一次查询,用户需要时间查看查询的结果是否是自己想要的.例如一次订单提交,用户需要时间核对自己填写的信息是否正确等. 也就是说用户在做某些操作时, ...

  8. LoadRunner 技巧之 思考时间设置

    LoadRunner 技巧之 思考时间设置 用户访问某个网站或软件,一般不会不停地做个各种操作,例如一次查询,用户需要时间查看查询的结果是否是自己想要的.例如一次订单提交,用户需要时间核对自己填写的信 ...

  9. 算法时间复杂度、空间复杂度(大O表示法)

    什么是算法? 计算机是人的大脑的延伸,它的存在主要是为了帮助我们解决问题. 而算法在计算机领域中就是为了解决问题而指定的一系列简单的指令集合.不同的算法需要不同的资源,例如:执行时间或消耗内存. 如果 ...

随机推荐

  1. How to spend you day ?

    如果这是你生命中的最后的一天,你该如何去过好它呢? 不要浪费你生命中的每一分,每一秒!!!

  2. HQL中左连接,右连接,内连接

    左连接:left join 如A left join B 即取出A表的所有数据,由on条件关联的B表数据,有则显示,没有则为空: 右连接:right join 如 A right join B 即取出 ...

  3. jsp的Get 与 SET的区别

    getParameter:获取前个页面的数据,此方法获取的数据是从前台提交过来的 getAttribute:是获取setAttribute存储的数据 ========================= ...

  4. CodeForces 591A Wizards' Duel

    水题 #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> us ...

  5. Codevs 5590 A+B 问题 超级版

    5590 A+B 问题 超级版 时间限制: 1 s 空间限制: 1000 KB 题目等级 : 青铜 Bronze 题目描述 Description 不用+-*/%计算A+B 输入描述 Input De ...

  6. ecos的app生命周期

    5种变迁,安装.更新.启动.暂停.卸载,每个app都可以自行维护每种变迁 方法简单,只需要在app/$app_name目录下定义task.php文件 <?php class desktop_ta ...

  7. 用eclipse还是myeclipse好

    工欲善其事,必先利其器.工具,只是一个达成目的的手段,如果你的目的是更关心业务逻辑和开发效率,使用myeclipse.如果你的目的是对eclipse的插件的安装,使用更加了解,业余可以研究一下ecli ...

  8. DIV+CSS 常见问题及解决办法整理

    http://blog.shaogroup.com/divcss-%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E5%8F%8A%E8%A7%A3%E5%86%B3%E5% ...

  9. Cocos2dx 学习笔记整理----开发环境搭建

    最近在学习cocos2dx,预备将学习过程整理成笔记. 需要的工具和环境整理一下: 使用的版本 cocos2dx目前已经出到了v3.1.1,学习和项目的话还是用2.2.3为宜,毕竟不大想做小白鼠,并且 ...

  10. ASer Python学习笔记

    最近又开始学习python了,希望自己能坚持下去. 我看的书是thinkingpython,是在豆瓣的python学习组看到的. 连续看了3,4天左右,然后尝试着写了个小程序,复制文件的,代码如下: ...