基数排序,计数排序是常用的几种非比较排序,之前涉及到的排序方式基本都是基于比较的,复杂度最优的情况是O(nlogn),而非比较排序可以实现线性时间完成排序。但是相应的空间复杂度会有所增加。
桶排序(计数排序) :对于序列中的每一个元素,计算得到小于该元素的元素个数,从而确定了该元素在最终输出元素的位置。计数排序有一个非常重要的地方就是:假定数据的范围是在某个区间内,如果数据的大小相差非常大的话,那么计数排序是不适用的。比如三个数1,11111111,222222222222,那么在计数排序中开辟的空间消耗和时间消耗是非常不值得的。
代码如下:
public class BucketSort{
public static void buckerSort(int []arr){
if (arr==null||arr.length==0) {
return ;
}
int max=Integer.MIN_VALUE;
for (int i=0; i<arr.length;i++ ) {
max=Math.max(max.arr[i]);
}
int []bucket=new int[max+1];
for (int i=0; i<arr.length;i++ ) {
bucket[arr[i]]++;
}
int i=0;
for (int j=0; j<bucket.length;j++ ) {
while(bucket[j]-->0){
arr[i++]=j;
}
}
}
}
基数排序比较复杂,基数排序最初是用在打孔卡片制表机上的一种排序算法,由Herman Hollerith发明,也就是IBM的创始人。基数排序从最低为开始来排序的,从低位到高位,按位排序,按位排序必须是稳定的。
代码:
class RadixSort{
public static void radixSort(int []arr){
if (arr==null||arr.length==0) {
return ;
}
radixSort(arr,0,arr.length-1,maxbits(arr));
}
public static int maxbits(int []arr){
int max=Integer.MIN_VALUE;
for (int i=0; i<arr.length; i++) {
max=Math.max(max,arr[i]);
}
int res=0;
while(max!=0){
res++;
max/=10;
}
return res;
}
public static void radixSort(int arr[],int begin,int end,int digit){
//定义好基数为10
final int radix=10;
int i=0,j=0;
int []count=new int[radix];
int []bucket=new int [end-begin+1];
for (int d=1; d<=digit; d++) {
//对数字的每一位做排序
for (i=0; i<radix; i++) {
count[i]++;
}
for (i=begin; i<end; i++) {
j=getDigit(arr[i],d);
count[j]++;
}
for (i=1; i<radix; i++) {
count[i]=count[i]+count[i-1];
}
for (i=end; i>=begin; i--) {
j=getDigit(arr[i],d);
bucket[count[j]-1]=arr[i];
count[j--];
}
for (i=begin,j=0; i<end; i++,j++) {
arr[i]=bucket[j];
}
}
}
public static int getDigit(int x,int d){
return ((x/((int)Math.pow(10,d-1)))%10);
}
}
计数排序的一种应用:求数组排序后相邻两个数的最大差值。这里将数字放入桶中,以大于0,小于100的数为例。我们可以根据题目需要分出10个桶,第一个桶放0~9范围内的数,第二个10~19范围内的数,......依次类推。同一个桶内的数字之差一定小于两个桶之间的差。那么又因为数组是经过排序的。所以最大差值一定出现在(前一个桶的最大值与其之后的桶的最小值之差)之间。我们用一个大小为10个数组存这些值之间的差。然后找出最大值即可。
public class MaxGap {
public static int maxGap(int []nums){
if (nums==null||nums.length<2) {
return 0;
}
int len=nums.length;
int min=Integer.MAX_VALUE;
int max=Integer.MIN_VALUE;
for (int i = 0; i < len; i++) {
//求出数组中的最大值与最小值。
min=Math.min(min, nums[i]);
max=Math.max(max, nums[i]);
}
if (max==min) {
return 0;
}
//默认为false
boolean hasNum[]=new boolean[len+1];
int []maxs=new int [len+1];
int []mins=new int [len+1];
int bid=0;
for (int i = 0; i < len; i++) {
//得出同一个桶内的最大值最小值即可
bid=bucket(nums[i],len,min,max);
maxs[bid]=hasNum[bid]?Math.max(maxs[bid], nums[i]):nums[i];
mins[bid]=hasNum[bid]?Math.min(mins[bid], nums[i]):nums[i];
}
int res=0;
int lastMax=maxs[0];
int i=1;
//最大差值是前一个桶的最大值与当前桶的最小值之间的差
for (; i < len; i++) {
if (hasNum[i]) {
res=Math.max(res, mins[i]-lastMax);
lastMax=maxs[i];
}
}
return res;
}
//根据值大小判断存储的位置
private static int bucket(long num, long len, long min, long max) {
return (int) ((num-min)*len/(max-min));
}
}