计数排序(Counting sort)是一种稳定的线性时间排序算法。该算法于1954年由 Harold H. Seward 提出。计数排序使用一个额外的数组 ,其中第i个元素是待排序数组中值等于的元素的个数。然后根据数组 来将中的元素排到正确的位置。

计数排序的特征

当输入的元素是 之间的整数时,它的运行时间是。计数排序不是比较排序,因此不被 的下界限制。

由于用来计数的数组 的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序算法中,能够更有效的排序数据范围很大的数组。

通俗地理解,例如有10个年龄不同的人,统计出有8个人的年龄比A小,那A的年龄就排在第9位,用这个方法可以得到其他每个人的位置,也就排好了序。当然,年龄有重复时需要特殊处理(保证稳定性),这就是为什么最后要反向填充目标数组,以及将每个数字的统计减去1。算法的步骤如下:

  • 1.找出待排序的数组中最大和最小的元素
  • 2.统计数组中每个值为的元素出现的次数,存入数组 的第
  • 3.对所有的计数累加(从 中的第一个元素开始,每一项和前一项相加)
  • 4.反向填充目标数组:将每个元素放在新数组的第项,每放一个元素就将减去1

Java语言的实现

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
public class CountingSort {
public static void main(String[] args) {
int[] A = CountingSort.countingSort(new int[]{16, 4, 10, 14, 7, 9, 3, 2, 8, 1});
Utils.print(A);
}

public static int[] countingSort(int[] A) {
int[] B = new int[A.length];
// 假设A中的数据a'有,0<=a' && a' < k并且k=100
int k = 100;
countingSort(A, B, k);
return B;
}

private static void countingSort(int[] A, int[] B, int k) {
int[] C = new int[k];
// 计数
for (int j = 0; j < A.length; j++) {
int a = A[j];
C[a] += 1;
}
Utils.print(C);
// 求计数和
for (int i = 1; i < k; i++) {
C[i] = C[i] + C[i - 1];
}
Utils.print(C);
// 整理
for (int j = A.length - 1; j >= 0; j--) {
int a = A[j];
B[C[a] - 1] = a;
C[a] -= 1;
}
}
}


//针对c数组的大小,优化过的计数排序
public class CountSort{
public static void main(String []args){
//排序的数组
int a[] = {100, 93, 97, 92, 96, 99, 92, 89, 93, 97, 90, 94, 92, 95};
int b[] = countSort(a);
for(int i : b){
System.out.print(i + " ");
}
System.out.println();
}
public static int[] countSort(int []a){
int b[] = new int[a.length];
int max = a[0], min = a[0];
for(int i : a){
if(i > max){
max = i;
}
if(i < min){
min = i;
}
}
//这里k的大小是要排序的数组中,元素大小的极值差+1
int k = max - min + 1;
int c[] = new int[k];
for(int i = 0; i < a.length; ++i){
c[a[i]-min] += 1;//优化过的地方,减小了数组c的大小
}
for(int i = 1; i < c.length; ++i){
c[i] = c[i] + c[i-1];
}
for(int i = a.length-1; i >= 0; --i){
b[--c[a[i]-min]] = a[i];//按存取的方式取出c的元素
}
return b;
}
}


//另一種參考,
//缺點:不適合數據落差大、浮點數,數據落差大會生成大a數組,數少用其他演算更好。
//優點:線性快,固定重複巨量數據適用,沒有更快,理論,只統計,不做多餘運算或搬移。
import java.util.Arrays;
import java.util.Random;
public class Csoft {
public static void main(String[] args) {
// int arr[] = { -535000000, 0, -372, -299,830000};
// int arr[] = {100, 93, 97, 92, 96, 99, 92, 89, 93, 97, 90, 94, 0, -1,-1,-95};
// int arr[] = Random_Numbers(500000000);
int arr[] = Random_Numbers(20);
System.out.println(Arrays.toString(arr));
new Csoft(arr);
System.out.println(Arrays.toString(arr));
}

private int min;
Csoft(){}
Csoft(int[] arr) {
b(arr);
}
public static int[] b(int[] arr) {
int max = 0, min = 0;
for (int i = 0; i < arr.length; i++) {
max = arr[i] > arr[max] ? i : max;
min = arr[i] < arr[min] ? i : min;
}
int k = -arr[min]; //k=基數
max = arr[max] + 1;
int[] a = new int[max + k];
for (int i = 0; i < arr.length; i++) {
int o = arr[i] + k;
a[o]++;
}
int t = 0;
for (int j = 0; j < a.length; j++) {
if (a[j] > 0) {
for (int i = 0; i < a[j]; i++) {
arr[t] = j - k;
t++;
}
}
}
return arr;
}
public static int[] Random_Numbers(int num) { //亂數負數
Random r = new Random();
int[] c = new int[num];
for (int i = 0; i < num; i++) {
c[i] = r.nextInt(1000) - 500;
}
return c;
}
}

C语言的实现

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
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void print_arr(const int *arr, const int n) {
printf("%d", arr[0]);
for (int i = 1; i < n; i++)
printf(" %d", arr[i]);
printf("\n");
}

void counting_sort(const int *ini_arr, int *sorted_arr, const int n, const int max_val) {
int *count_arr = (int *) calloc(max_val, sizeof(int));
for (int i = 0; i < n; i++)
count_arr[ini_arr[i]]++;
for (int i = 1; i < max_val; i++)
count_arr[i] += count_arr[i - 1];
for (int i = n; i > 0; i--)
sorted_arr[--count_arr[ini_arr[i - 1]]] = ini_arr[i - 1];
free(count_arr);
}

int main(int argc, char **argv) {
int n = 10;
int max_val = 100;
int *arr = (int *) calloc(n, sizeof(int));
int *sorted_arr = (int *) calloc(n, sizeof(int));
srand(time(0));
for (int i = 0; i < n; i++)
arr[i] = rand() % max_val;
printf("ini_array: ");
print_arr(arr, n);
counting_sort(arr, sorted_arr, n, max_val);
printf("sorted_array: ");
print_arr(sorted_arr, n);
free(arr);
free(sorted_arr);
return 0;
}

javascript实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Array.prototype.countSort = function() {
const C = []
for(let i = 0; i < this.length; i++) {
const j = this[i]
C[j] >= 1 ? C[j] ++ : (C[j] = 1)
}
const D = []
for(let j = 0; j < C.length; j++) {
if(C[j]) {
while(C[j] > 0) {
D.push(j)
C[j]--
}
}
}
return D
}
const arr = [11, 9, 6, 8, 1, 3, 5, 1, 1, 0, 100]
console.log(arr.countSort())

Golang的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func countingSort(arr []int, minvalue, maxValue int) []int {
bucketLen := maxValue - minvalue + 1
bucket := make([]int, bucketLen)
for _, v := range arr {
bucket[v-minvalue]++
}
result := make([]int, len(arr))
index := 0
for k, v := range bucket {
kk := k + minvalue
for j := v; j > 0; j-- {
result[index] = kk
index++
}
}
return result

}

Python3的实现

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
# -*- coding: utf-8 -*-

def count_sort(a: list) -> list:
a_min: int = min(a)
k: int = max(a) - a_min + 1
c: list = [0 for _ in range(k)]

for i in a:
c[i - a_min] += 1

for i, v in enumerate(c):
if i == 0:
continue
c[i] = v + c[i-1]

result: list = [None for _ in range(len(a))]
for i in a:
result[c[i - a_min] - 1] = i
c[i - a_min] -= 1

return result


if __name__ == '__main__':
print(count_sort([652, 721, 177, 977, 24, 17, 126, 515, 442, 917]))

原文地址:https://zh.wikipedia.org/wiki/%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F

知识共享 署名-相同方式共享 3.0协议之条款下提供