计数排序
计数排序的核心在于将输入的数据值转化为额外开辟的数组空间中的下标。
作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。计数排序(Counting sort)是一种稳定的排序算法。计数排序使用一个额外的数组B,其中第i个元素是待排序数组A中值与i相关的元素的个数。然后根据数组B来将A中的元素排到正确的位置。
它只能对整数进行排序。
算法思路
- 首先找到待排序列的最大值和最小值,通过最大值最小值创建额外数组空间,长度为max-min+1
- 遍历待排序列,将大小为i的元素放到与i相关下标的额外数组中,额外数组对象中存放i大小元素的个数。
- 之后反向通过额外数组,输出元素,就已经排好序了。
代码
public class Countingsort {
public int[] CountingSort(int[] a) {
if(a.length < 2)
return a;
int min = a[0];
int max = a[0];
for(int i = 1; i < a.length; i++) { //找到序列中最大值最小值
if(a[i] < min)
min = a[i];
if(a[i] > max)
max = a[i];
}
int[] b = new int[max-min+1]; //建立另外数组,存放待排序列的数值个数
for(int i = 0; i < a.length ; i++) { //数组下标为待排序列的数值的索引,空间里存放这个数值的个数
b[a[i]-min]++;
}
int index = 0;
int k = 0;
while(index < a.length ) { //输出元素
if(b[k] != 0) { //根据数组下标有序,输出的数值也是有序
a[index] = k+min;
index++;
b[k]--;
}
else
k++;
}
return a;
}
算法分析
计数排序是稳定的
本文的计数排序通过额外数组的下标和存储空间的个数,来对序列进行排序。输出时通过下标与最小值的相加,来输出元素。不是真正意义上将待排序列的元素进行输出。
改善,将真正的元素放到新数组中成为有序
额外数组中存放的是关于它下标的相同元素值的个数,我们把它与前面的相加,就会得到 这些元素在排列好的数组中的位置。
通过位置,把原来待排序列的数组从后往前遍历,放到新数组的正确位置上(从后往前,保证排序的稳定,因为个数相加后,相同元素的话,位置指向的是后面那个)
当输入的元素是n 个0到k之间的整数时,它的运行时间是 O(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。由于用来计数的数组的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。最佳情况:T(n) = O(n+k) 最差情况:T(n) = O(n+k) 平均情况:T(n) = O(n+k) 。
桶排序
桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据根据每个桶的区间分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。然后遍历桶依次取出桶中的元素即可完成排序。
和计数排序类似,桶排序也对输入数据作了某种假设,因此它的速度也很快。具体来说,计数排序假设了输入数据都属于一个小区间内的整数,而桶排序则假设输入数据是均匀分布的,即落入每个桶中的元素数量理论也是差不多的,不会出现很多数落入同一个桶内的情况。
算法思路
- 自己设置一个桶的区间size,比如3,就只能最大值和最小值不超过3
- 找到待排序列最大值最小值,通过(max-min)/size+1 得出桶的个数
- 创建存放桶的数组,它的下标代表着桶的标记,待排序列中的元素通过这个标记来选择正确的桶(如何选择正确的桶可以自己进行设计)
- 元素进入正确的桶中,进行排序,可以使用其他排序方法也可以递归使用桶排序。
- 将桶中的元素顺序输出
代码
public class Bucketsort {
public int[] BuckerSort(int[] a, int size) { // size,桶装数值的区间
if (a.length < 2)
return a;
int min = a[0];
int max = a[0];
for (int i = 1; i < a.length; i++) { // 找到序列中最大值最小值
if (a[i] < min)
min = a[i];
if (a[i] > max)
max = a[i];
}
int bucketCount = (max - min) / size + 1; // 桶的个数
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount); // 建立桶的集合
for (int i = 0; i < bucketCount; i++)
bucketArr.add(new ArrayList<Integer>()); // 创建桶并放入桶的集合内
for (int i : a) {
int index = (i - min) / size; // index 指 元素应该到哪个桶里面 是桶集合里面桶的下标
inserSort(bucketArr.get(index), i); // 插入元素进 指定的桶并排序
}
// 将桶的元素一个个取出来
int index = 0;
for (ArrayList<Integer> bucket : bucketArr) {
for (int i : bucket)
a[index++] = i;
}
return a;
}
public void inserSort(ArrayList<Integer> bucket, int i) {
if (bucket.isEmpty() == true) //桶为空,直接插入i
bucket.add(i);
bucket.add(i);
int a = bucket.size() - 2; //a 是前面位置元素下标,就是原来数组最后元素下标
while (a >= 0) {
if (bucket.get(a) > i) {
bucket.set(a + 1, bucket.get(a));
a--;
} else
break;
bucket.set(a + 1, i);
}
}
算法分析
平均情况下,桶排序的时间复杂度为 O(n+k)。
最坏情况下,所有数据都放到同一个桶内,桶排序的时间复杂度为 O(n^2) 或 O(n * lg n),这取决于桶内元素自排序的算法。
基数排序
基数排序也是非比较的排序算法,对数值的每一个位数进行排序,从最低位开始排序;基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。基数排序基于分别排序,分别收集,所以是稳定的。
算法思路
- 首先,找到待排序列的最大值,算出最大值的位数
- 将元素的个位数 拿出来进行排序 ,排列好收集起来
- 将元素的十位数拿出来进行排序,排好收集起来
- 以此类推,直到最高位,最终的序列也就有序了。
每一次的位数排序,可以参照计数排序,额外那个数组空间就是10,下标0~9
代码
public class Radixsort {
public int[] RadixSort(int[] a) {
if (a.length < 2)
return a;
int max = a[0];
for (int i = 1; i < a.length; i++) {
if (a[i] > max)
max = a[i];
}
int maxd = 0; // 找最大数的位数
while (max != 0) {
max = max / 10;
maxd++;
}
for (int k = 1; k <= maxd; k++) {
int[] count = new int[10];
int[] bucket = new int[a.length];
for (int i = 0; i < a.length; i++)
count[getk(a[i], k)]++;
for (int i = 1; i < 10; i++)
// 本来是count里面值是与下标i有关系数值的元素个数,现在指向新数组的位置,
count[i] = count[i] + count[i - 1];
for (int i = a.length - 1; i >= 0; i--) //从后到前 放入桶,是为了排序的稳定
{
int j = getk(a[i], k);
bucket[count[j] - 1] = a[i];
count[j]--;
}
for (int i = 0, j = 0; i < a.length; i++, j++) // 将桶中的数据取出来,赋值给a数组
{
a[i] = bucket[j];
}
}
return a;
}
public int getk(int a, int k) { // 返回数据i第k位的数 第一位是个位数
return (a / (10 ^ (k - 1))) % 10;
}
算法分析
最佳情况:T(n) = O(n * k) 最差情况:T(n) = O(n * k) 平均情况:T(n) = O(n * k)。基数排序有两种方法:MSD 从高位开始进行排序 LSD 从低位开始进行排序 。
计数排序:每个桶只存储单一键值
桶排序:每个桶存储一定范围的数值
基数排序:根据键值的每位数字来分配桶
桶的下标就是能索引到元素的值
计数是一对一,桶排序是一个下标可以对一定范围的元素值
基数排序一个桶代表一个位数
先排序好一个桶,收集起来再继续通过位数放入桶 ,然后排序,继续。。