基数排序

基数排序

一、算法描述

1.1 核心思想

  • 数据有高低位之分,位之间有递进关系
  • 高位相等的情况下,才去对比低位大小
  • 按照低位到高位的顺序,使用稳定排序算法对每一位排序

1.2 细节解释

数据有高低位之分,比如十进制数字 1232,字符串 dwsc,二进制 1001 等等。

这些类型的数据都有高低位,而且是高位大的数据肯定比高位小的数据大。

比如 123234123 最高位 1 小于 234 最高位 2,所以 123 < 234

  • 基数排序就是利用数据的每一位进行排序,最终得到有序序列的

比如说,数组 [12, 3, 154, 78, 9, 245, 35, 92]

因为数字长度不同,所以首先将所有数字补 0 进行长度对齐:

1
2
3
4
5
6
7
8
012
003
154
078
009
245
035
092

然后按照从低位到高位进行排序:

先按“个位”排序:

1
2
3
4
5
6
7
8
9
10
当前数组     取出“个”位     按“个”位排序

012 xx2 012
003 xx3 092
154 xx4 003
078 => xx8 => 154
009 xx9 245
245 xx5 035
035 xx5 078
092 xx2 009

再按“十位”排序:

1
2
3
4
5
6
7
8
9
10
当前数组     取出“十”位     按“十”位排序

012 x1x 003
092 x9x 009
003 x0x 012
154 => x5x => 035
245 x4x 245
035 x3x 154
078 x7x 078
009 x0x 092

再按“百位”排序:

1
2
3
4
5
6
7
8
9
10
当前数组     取出“百”位     按“百”位排序

003 0xx 003
009 0xx 009
012 0xx 012
035 => 0xx => 035
245 2xx 078
154 1xx 092
078 0xx 154
092 0xx 245

每个“位”都排完之后,就是最终的排序结果了:

1
[3, 9, 12, 35, 78, 92, 154, 245]

基数排序的关键在于从低位往高位排序,这样能保证高位排序是最后的。

二、算法实现

2.1 计算数据的基数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 找到最大值
int max = Integer.MIN_VALUE;
for (int a : arr) {
if (a > max) {
max = a;
}
}

// 算出基数(即位数)
int radix = 0;
while(max > 0) {
max = max / 10;
radix++;
}

2.2 从低位到高位排序

1
2
3
4
5
6
// 从低位到高位对数组进行排序
int exp = 1;
for (int i = 0; i < radix; i++) {
countSort(arr, exp);
exp *= 10;
}

对每一位进行排序,则使用计数排序(或者其他稳定排序算法也行)。

三、算法分析

3.1 时间复杂度

  • 最好时间复杂度:O(n)
  • 最坏时间复杂度:O(n)
  • 平均时间复杂度:O(n)

3.2 空间复杂度

  • 空间复杂度:O(n)
  • 非原地算法

3.3 稳定性

  • 稳定排序算法

四、适用场景

  • 数据可以分割出“位”来比较
  • 数据有高低位之分,位之间有递进关系
  • 每一位的数据范围不能太大,要可以用线性排序算法来排序

附录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* 基数排序
* <p>
* 时间复杂度:O(n)
* <p>
* 空间复杂度:O(n)
* <p>
* 稳定性:稳定
*
* @author weijiaduo
* @since 2022/9/6
*/
public class RadixSort implements Sort {

@Override
public void sort(int[] arr) {
// 找到数组最大值
int max = Integer.MIN_VALUE;
for (int a : arr) {
if (a > max) {
max = a;
}
}

// 计算基数大小
int radix = 0;
while(max > 0) {
max = max / 10;
radix++;
}

// 从低位到高位对数组进行排序
int exp = 1;
for (int i = 0; i < radix; i++) {
countSort(arr, exp);
exp *= 10;
}
}

/**
* 计数排序
*
* @param arr 数组
* @param exp 指数
*/
private void countSort(int[] arr, int exp) {
// 统计每个数字(0-9)的次数
int[] counts = new int[10];
for (int a : arr) {
counts[(a / exp) % 10]++;
}

// 累计数字的次数和
for (int i = 1; i < counts.length; i++) {
counts[i] += counts[i - 1];
}

// 更新排序结果到原数组
int[] copy = Arrays.copyOf(arr, arr.length);
for (int i = copy.length - 1; i >= 0; i--) {
int index = (copy[i] / exp) % 10;
counts[index]--;
arr[counts[index]] = copy[i];
}
}

}
作者

jiaduo

发布于

2022-09-06

更新于

2023-10-06

许可协议