三种线性排序算法:桶排序、计数排序、基数排序
线性排序算法(Linear Sort):这些排序算法的时间复杂度是线性的O(n),是非比较的排序算法
桶排序(Bucket Sort)
将要排序的数据分到几个有序的桶里,每个桶里的数据再单独进行排序,桶内排完序之后,再把桶里的数据按照顺序取出,组成的序列就是有序的了
桶排序比较适合用在外部排序中,比如磁盘中的数据需要 排序而内存有限,没有办法将数据全部加载的情况
桶排序步骤:
1.找出待排序数组中的最大值max、最小值min
2. 动态数组ArrayList 作为桶,桶里放的元素也用 ArrayList 存储。桶的数量为(max-min)/arr.length+1
3.遍历数组 arr,计算每个元素 arr[i] 放的桶
4.每个桶各自排序
5.遍历桶数组,把排序好的元素放进输出数组
图解:
代码:
/**
* 桶排序
*
* @param arr 待排数组
*/
public static void bucketSort(int[] arr) {
int min = arr[0];
int max = arr[0];
// 获取数据范围
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
// 确定桶数
int count = (min + max) / arr.length + 1;
ArrayList<ArrayList<Integer>> bucket = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
bucket.add(new ArrayList<>());
}
// 将元素放入桶
for (int i = 0; i < arr.length; i++) {
int num = (arr[i] - min) / arr.length;
bucket.get(num).add(arr[i]);
}
// 将桶中数据进行排序(排序已经完成)
for (ArrayList<Integer> arrayList : bucket) {
Collections.sort(arrayList);
}
// 将桶中数据导入待排序的数组中(只是为了显示方便)
int k = 0;
for (int i = 0; i < bucket.size(); i++) {
for (int j = 0; j < bucket.get(i).size(); j++) {
arr[k++] = bucket.get(i).get(j);
}
}
}
计数排序(Counting Sort)
桶排序的一种特殊情况,当要排序的n个数据,所处的范围并不大的时候,比如最大值是k,我们就可以把数据划分成k个桶,每个桶的表数据值都是相同的,省掉了桶内排序的步骤,在显示生活中也存在这样的案例,比如高考省榜,就是按照这种排序方式进行排列的(主要适用于数据比较集中的情况)
计数排序步骤:
1 求出待排序列的最大值和最小值
2 设定一个计数数组,用来记录每个元素出现的次数
3 记录待排序列在计数数组中的位置和次数
4 将计数数组中的数据灌入待排序列中
图解:
计数排序在生活中的应用:—— 高考省榜
代码:
/**
* 计数排序
*
* @param arr 待排数组
*/
public static void countingSort(int[] arr) {
int min = arr[0];
int max = arr[0];
// 获取数据范围
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
// 创建计数数组count,用来记录数据出现的次数
int[] count = new int[max - min + 1];
for (int i = 0; i < arr.length; i++) {
count[arr[i] - min]++;
}
// 将计数数组中的数据灌入待排序数组
int k = 0;
for (int i = 0; i < count.length; i++) {
if (count[i] != 0) {
for (int j = 0; j < count[i]; j++) {
arr[k++] = i + min;
}
}
}
}
基数排序(Radix Sort)
基数排序对要排序的数据有要求,需要可以分割出独立的”位”进行比较,而且位与位之间有递进关系,如果a数据的高位比b数据大,那剩下的地位就不用比较了,除此之外每一位的数据范围不能太大,要可以用线性排序算法来排序,否则基数排序的时间复杂度就无法做到O(n)
基数排序的步骤:
1 找出待排序列的未排高位
2 用线性排序按照未排高位进行线性排序,然后重复1过程,直至序列全部有序
图解:
代码:
/**
* 基数排序
*
* @param arr 待排数组
* @param len 待排位数
*/
public static void radixSort(int[] arr, int len) {
// 除数,从8位0开始,先比较高位
int divisor = (int) Math.pow(10, len);
// 确定桶数,桶的数量可设为10,因为位数的取值为0-9,并初始化桶
ArrayList<ArrayList<Integer>> bucket = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
bucket.add(new ArrayList<>());
}
for (int i = 0; i < arr.length; i++) {
// 只取个位
int c = (arr[i] / divisor) % 10;
bucket.get(c).add(arr[i]);
}
// 将桶中数据进行排序(排序已经完成)
for (ArrayList<Integer> arrayList : bucket) {
Collections.sort(arrayList);
}
// 将桶中数据导入待排序的数组中
int k = 0;
for (int i = 0; i < bucket.size(); i++) {
for (int j = 0; j < bucket.get(i).size(); j++) {
arr[k++] = bucket.get(i).get(j);
}
}
}
测试类及生成随机数组,随机订单的方法(基数排序的数据是50个8位订单号)
/**
* 生成一个长度5-10的随机数组
*
* @return 随机数组
*/
private static int[] initArray() {
int len = 5 + new Random().nextInt(6);
int[] arr = new int[len];
for (int i = 0; i < arr.length; i++) {
arr[i] = new Random().nextInt(100);
}
return arr;
}
/**
* 为基数排序法准备的8位随机订单,50组数据
*
* @return 随机订单数组
*/
private static int[] initArrayForRadixSort() {
String str = "";
int[] arr = new int[50];
Random random = new Random();
for (int i = 0; i < arr.length; i++) {
// 保证订单首位数不为0,所以随机0-9生成后7位,随机1-9生成第一位
for (int j = 0; j < 7; j++) {
str += random.nextInt(10);
}
str = 1 + random.nextInt(9) + str;
arr[i] = Integer.valueOf(str);
str = "";
}
return arr;
}
public static void main(String[] args) {
// 基数排序
int[] arr = initArrayForRadixSort();
System.out.println("排序之前的数组是:" + Arrays.toString(arr));
int count = (arr[0] + "").length() - 1;
for (int i = count; i < arr.length; i++) {
countingSort(arr, i);
}
System.out.println("排序之后的数组是:" + Arrays.toString(arr));
// 非基数排序
int[] arr2 = initArray();
System.out.println("排序之前的数组是:" + Arrays.toString(arr2));
quockSort(arr2);
System.out.println("排序之后的数组是:" + Arrays.toString(arr2));
}
测试结果,亲测有效:
桶排序:
排序之前的数组是:[31, 64, 46, 94, 29, 99, 82, 48, 84]
排序之后的数组是:[29, 31, 46, 48, 64, 82, 84, 94, 99]
计数排序:
排序之前的数组是:[11, 20, 56, 2, 47, 75, 66, 21, 3, 74]
排序之后的数组是:[2, 3, 11, 20, 21, 47, 56, 66, 74, 75]
基数排序:
排序之前的数组是:[54920007, 10744441, 24003637, 22072166, 12466348, 17322066, 54489715, 64847150, 48366735, 80287146, 71463179, 82992316, 74996232, 37427568, 45629716, 71177033, 81944062, 74519384, 91743758, 68246062, 23798432, 96894141, 69555229, 49967525, 52587116, 43508294, 47269398, 97352691, 82058781, 67895293, 11909501, 30869797, 47345049, 50743493, 25530908, 85030153, 16247011, 76036389, 10846207, 88061686, 17767234, 25997527, 98880748, 26860131, 98633987, 88621984, 89076873, 70419980, 95812196, 21624491]
排序之后的数组是:[10744441, 10846207, 11909501, 12466348, 16247011, 17322066, 17767234, 21624491, 22072166, 23798432, 24003637, 25530908, 25997527, 26860131, 30869797, 37427568, 43508294, 45629716, 47269398, 47345049, 48366735, 49967525, 50743493, 52587116, 54489715, 54920007, 64847150, 67895293, 68246062, 69555229, 70419980, 71177033, 71463179, 74519384, 74996232, 76036389, 80287146, 81944062, 82058781, 82992316, 85030153, 88061686, 88621984, 89076873, 91743758, 95812196, 96894141, 97352691, 98633987, 98880748]