桶排序:
1. 原理: 根据数据范围,分成若干个数据段的桶,通过遍历讲数据放到对应的桶中。每个桶里都进行快排或归并。
2. 时间复杂度: 最好o(n), 最坏o(nlogn), 平均o(n),一般桶分的越细越多复杂度就会最好。
3. 内存消耗: o(n)
4. 稳定性: 取决于每个桶的排序方式,快排就不稳定,归并就稳定。
5. 适用场景: 数据范围不大的。内存吃紧的,如磁盘的读写可以分成多个小文件并对每个小文件排序,然后直接写到大文件里,这个时候内存消耗不再是o(n)了。
计数排序:
1. 原理: 特殊的桶排序,即每个下标代表一个数据范围,其值就是这个数据的个数。
2. 时间复杂度: 都是o(n)
3. 内存消耗: o(n)
4. 稳定性: 稳定,只要整理最后结果时从后开始遍历即可。
5. 适用场景: 数据范围不大的,如年龄排序。
// 计数排序,a 是数组,n 是数组大小。假设数组中存储的都是非负整数。
public void countingSort(int[] a, int n) {
if (n <= 1) return;
public void countingSort(int[] a, int n) {
if (n <= 1) return;
// 查找数组中数据的范围
int max = a[0];
for (int i = 1; i < n; ++i) {
if (max < a[i]) {
max = a[i];
}
}
int max = a[0];
for (int i = 1; i < n; ++i) {
if (max < a[i]) {
max = a[i];
}
}
int[] c = new int[max + 1]; // 申请一个计数数组 c,下标大小 [0,max]
for (int i = 0; i <= max; ++i) {
c[i] = 0;
}
for (int i = 0; i <= max; ++i) {
c[i] = 0;
}
// 计算每个元素的个数,放入 c 中
for (int i = 0; i < n; ++i) {
c[a[i]]++;
}
for (int i = 0; i < n; ++i) {
c[a[i]]++;
}
// 依次累加
for (int i = 1; i <= max; ++i) {
c[i] = c[i-1] + c[i];
}
for (int i = 1; i <= max; ++i) {
c[i] = c[i-1] + c[i];
}
// 临时数组 r,存储排序之后的结果
int[] r = new int[n];
// 计算排序的关键步骤,有点难理解
for (int i = n - 1; i >= 0; --i) {
int index = c[a[i]]-1;
r[index] = a[i];
c[a[i]]--;
}
int[] r = new int[n];
// 计算排序的关键步骤,有点难理解
for (int i = n - 1; i >= 0; --i) {
int index = c[a[i]]-1;
r[index] = a[i];
c[a[i]]--;
}
// 将结果拷贝给 a 数组
for (int i = 0; i < n; ++i) {
a[i] = r[i];
}
}
for (int i = 0; i < n; ++i) {
a[i] = r[i];
}
}
基数排序:
1. 原理: 对数据的每一位进行桶排序或计数排序,对每位排序后结果就是有序的。
2. 时间复杂度: 最好o(n), 最坏o(nlogn), 平均o(n)
3. 内存消耗: o(n)
4. 稳定性: 稳定。否则就排不成的。
5. 适用场景: 是在桶排序和计数排序基础上进行的,保证每位数据范围不大,并且位数也不是很多。