计数排序
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数
。
算法步骤:
- 找出待排序的数组中最大和最小的元素;
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
代码如下:
下述代码事实上还可以进行优化,即新建的tmp的大小可以限制为max-min+1。
public static int[] sort(int[] nums){
// int min=Integer.MAX_VALUE;
int max=-Integer.MAX_VALUE;
int[] res=new int[nums.length];
for (int i=0;i<nums.length;i++){
if (nums[i]>max)
max=nums[i];
// if (nums[i]<min)
// min=nums[i];
}
int[] tmp=new int[max+1];
for (int num:nums){
tmp[num]+=1;
}
int count=0;
for (int i=0;i<tmp.length;i++){
while (tmp[i]!=0) {
res[count++] = i;
tmp[i]--;
}
}
return res;
}
桶排序
桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序
(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。
算法步骤:
- 设置一个定量的数组当作空桶;
- 遍历输入数据,并且把数据一个一个放到对应的桶里去;
- 对每个不是空的桶进行排序;
- 从不是空的桶里把排好序的数据拼接起来。
public static double[] bucketSort(double[] arr) {
double max=-Integer.MAX_VALUE;
double min=Integer.MAX_VALUE;
int bucketNum=arr.length;
for (int i=0;i<arr.length;i++){
if (max < arr[i]){
max = arr[i];
}
if (arr[i] < min){
min = arr[i];
}
}
double d = max - min;
//初始化桶
List<LinkedList<Double>> bucketList = new ArrayList<>(bucketNum);
for (int i = 0; i < bucketNum; i++) {
bucketList.add(new LinkedList<>());
}
//将元素加入到桶中
for (int i=0;i<arr.length;i++){
int num=(int)((arr[i]-min)/(d/(bucketNum-1)));
bucketList.get(num).add(arr[i]);
}
for (int i=0;i<bucketNum;i++){
Collections.sort(bucketList.get(i));
}
//输出元素
int cnt=0;
for (List<Double> bucket:bucketList){
for (Double s:bucket){
arr[cnt++]=s;
}
}
return arr;
}
基数排序
这种是一种非比较排序的方法。按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
算法步骤:
- 取得数组中的最大数,并取得位数;
- arr为原始数组,从最低位开始取每个位组成radix数组;
- 对radix进行计数排序(利用计数排序适用于小范围数的特点);
public static int[] sort(int[] nums){
int max=-Integer.MAX_VALUE;
int exp;
for (int num:nums){
if (num>max)
max=num;
}
for (exp=1;max/exp>0;exp*=10){
int[] tmp=new int[nums.length];
//任何一位数只能在0-9
int[] buckets=new int[10];
for (int value:nums){
buckets[(value/exp)%10]++;
}
//这块进行累加的原因是,确保基数较大的元素能够排在后面,这个针对的是升序排序,然后原本的数组从后往前遍历。如果是降序排序,则基数数组的首位值最大,原数组从前往后遍历。
//上面这么做的目的是为了维持稳定性
for (int i = 1; i < 10; i++){
buckets[i] += buckets[i - 1];
}
for (int i=nums.length-1;i>=0;i--){
tmp[buckets[(nums[i]/exp)%10]-1]=nums[i];
buckets[(nums[i]/exp)%10]--;
}
System.arraycopy(tmp,0,nums,0,nums.length);
}
return nums;
}