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

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

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

针对给定的一组整数数据,在其中任意选取一个数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. Server对象

    Server是服务器对象,定义了一个与Web服务器相关的类,用于访问服务器上的资源. 属性 MachineName   获取服务器的计算机名.    返回本地计算机的名称 ScriptTimeout  ...

  2. zf-关于触摸屏子系统(查询机)的页面

    这个页面 一般是 touch/index.jsp 这个要记下来. -- 今天有个任务 是把查询机的UI页面给换掉

  3. Android] Android XML解析学习——方式比较

     [Android] Android XML解析学习——方式比较 (ZT)  分类: 嵌入式 (From:http://blog.csdn.net/ichliebephone/article/deta ...

  4. Java回调函数的理解

    所谓回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数.例如Win32下的窗口过程函数就是一个典型的回调函数.一般说来,C ...

  5. CodeForces 158B Taxi(贪心)

    贪心,注意优先级,4单独,3与1先匹配,2与2匹配(注意判断2有没有剩下),然后2与两个1匹配,最后4个1匹配就可以了. #include<iostream> #include<cs ...

  6. 打包Egret游戏为Chrome extension

    今天,本来是打算做一个Chrome扩展去爬取网站base64编码图片的. 在跟着图灵社区<Chrome扩展及应用开发>敲demo代码的过程中,发现chrome的扩展的结构理论上可以兼容所有 ...

  7. android开发中应该注意的问题

    1. Activity可继承自BaseActivity,便于统一风格与处理公共事件,构建对话框统一构建器的建立,万一需要整体变动,一处修改到处有效.   2. 数据库表段字段常量和SQL逻辑分离,更清 ...

  8. pscs6

    http://wenku.baidu.com/link?url=cO03xdo_GQDdFdeYTDD36ZrjeHarUu4IN-fSEoFAnDXmd5W0yKvzkNWY_vOKKIaKbCdB ...

  9. input输入框和 pure框架中的 box-sizing 值问题

    在使用pureCSS框架的时候,遇到一个问题. input输入框,我给他们设置了宽度和padding值,我发现,在火狐和谷歌上面发现,增加padding值并不会影响最终的宽度,而在IE6 7下则会影响 ...

  10. 重启OpenStack服务步骤

    [重启neutron服务] 控制节点:service openstack-nova-api restartservice openstack-nova-scheduler restartservice ...