排序问题思考(要求时间和空间复杂度尽可能的低)【Part 2】
继上篇博文,今天我将先介绍一下什么是计数排序,将计数排序描述清楚后,再进行后续的桶排序方法解决这个问题。
通常情况下,一提到排序,大家第一反应就是比较,其实,今天我要说的这个计数排序,不是基于比较的排序算法。
计数排序的主要思路(精髓):
针对给定的一组整数数据,在其中任意选取一个数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】的更多相关文章
- 栈(stack)、递归(八皇后问题)、排序算法分类,时间和空间复杂度简介
一.栈的介绍: 1)栈的英文为(stack)2)栈是一个先入后出(FILO-First In Last Out)的有序列表.3)栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的 ...
- 原生select默认显示为空胡fish覅神农大丰今年圣诞节奋笔疾书发撒可交付你说的尽快发那段时间南方大厦尽可能放你的所发生的你富家大室耐腐蚀的看法呢尽快发你上课积啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊撒啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊分你束带结发你看
下拉框默认为空: <select> <option value="" class="blank"></option> < ...
- CDQ分治学习思考
先挂上个大佬讲解,sunyutian1998学长给我推荐的mlystdcall大佬的[教程]简易CDQ分治教程&学习笔记 还有个B站小姐姐讲解的概念https://www.bilibili.c ...
- leetcode -- 二进制
leetcode -- 二进制 在学习编程语言的运算符时,大部分语言都会有与,或等二进制运算符,我在初期学习这些运算符的时候,并没有重点留意这些运算符,并且在后续的业务代码中也没有频繁的使用过,直到后 ...
- MySQL应用优化
MySQL应用优化 目录 MySQL应用优化 1.数据库连接池 2.减少对MySQL的访问 3.负载均衡 4.MySQL查询缓存优化 5.MySQL如何使用缓存 6.MySQL内存管理以及优化 原则 ...
- LoadRunner 思考时间与事务响应时间的区别与关系
LoadRunner 思考时间与事务响应时间的区别与关系 思考时间lr_think_time 就是一个事务要开始时思考的时间;比如 你要点击一个 登录按钮 我们都要点击这个按钮要先思考下 就是人为 ...
- LoadRunner ---思考时间设置
用户访问某个网站或软件,一般不会不停地做个各种操作,例如一次查询,用户需要时间查看查询的结果是否是自己想要的.例如一次订单提交,用户需要时间核对自己填写的信息是否正确等. 也就是说用户在做某些操作时, ...
- LoadRunner 技巧之 思考时间设置
LoadRunner 技巧之 思考时间设置 用户访问某个网站或软件,一般不会不停地做个各种操作,例如一次查询,用户需要时间查看查询的结果是否是自己想要的.例如一次订单提交,用户需要时间核对自己填写的信 ...
- 算法时间复杂度、空间复杂度(大O表示法)
什么是算法? 计算机是人的大脑的延伸,它的存在主要是为了帮助我们解决问题. 而算法在计算机领域中就是为了解决问题而指定的一系列简单的指令集合.不同的算法需要不同的资源,例如:执行时间或消耗内存. 如果 ...
随机推荐
- 基于ATmgea8单片机设计的加热控制系统(转)
源:http://blog.163.com/zhaojun_xf/blog/static/3005058020085102562729/ 1 引言 温度是工业生产中主要的被控参数之一,与之相关的各种温 ...
- 滑雪(ski)
滑雪(ski) 题目描述 Michael喜欢滑雪.这并不奇怪,因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道 ...
- iOS7以后的侧滑返回上一页
我们知道,iOS7以后,导航控制器默认带了侧滑返回功能,但是仅限于屏幕边缘.而且在你自定义leftBarButtonItem等之后侧滑效果就会消失.这种问题怎么解决呢? 首先,我们先来看看系统的这种手 ...
- 2.1Android界面View及ViewGroup(转)
2.1Android界面View及ViewGroup 2.1.0 View及ViewGroup类关系Android View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,V ...
- 生成makefile文件编译源文件
1.利用CodeBlock的cbp文件生成makefile文件 reverse@ubuntu:~/Desktop/CreateMakeFile$ ls cbp2make.linux-x86 freeg ...
- gdb 远程调试android进程 -转
什么是gdb 它是gnu组织开发的一个强大的unix程序调试工具,我们可以用它来调试Android上的C.C++代码. 它主要可以做4件事情: 随心所欲地启动你的程序. 设置断点,程序执行到断点处会停 ...
- [其他]volatile 关键字
用 volatile 关键字修饰函数 的作用是 告诉编译器该函数不会返回 , 让编译器能产生更好的代码 另外也能避免一些假警告信息,如未初始化的变量等
- 解决cookie 跨iframe
document.cookie = "name=caoyc;path=/"document.cookie = "age=13;path=/"//时间可以不要,但 ...
- js url编码函数
JavaScript中有三个可以对字符串编码的函数,分别是: escape,encodeURI,encodeURIComponent,相应3个解码函数:unescape,decodeURI,decod ...
- 阿里笔试js题
有一个数组,其中保存的都是小写英文字符串,现在要把它按照除了第一个字母外的字符的字典顺序(字典顺序就是按首字母从a-z顺序排列,如果首字母相同则按第二个字母……)排序,请编写代码: 例:[" ...