-
- 基数排序
- 计数排序
- 桶排序
基数排序,桶排序,计数排序是三种线性排序方法,突破了比较排序的O(nlogn)的限制。但是只适用于特定的情况。
基数排序
以下为维基百科的描述:
基数排序 :
将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant
digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。
基数排序的效率:
基数排序的时间复杂度是O(k·n),其中n是排序元素个数,k是数字位数。注意这不是说这个时间复杂度一定优于O(n·log(n)),k的大小取决于数字位的选择(比如比特位数),和待排序数据所属数据类型的全集的大小;k决定了进行多少轮处理,而n是每轮处理的操作数目。
以排序n个不同整数来举例,假定这些整数以B为底,这样每位数都有B个不同的数字,k =
logB(N),N是待排序数据类型全集的势。虽然有B个不同的数字,需要B个不同的桶,但在每一轮处理中,判断每个待排序数据项只需要一次计算确定对应数位的值,因此在每一轮处理的时候都需要平均n次操作来把整数放到合适的桶中去,所以就有:
k约等于logB(N) 所以,基数排序的平均时间T就是:
T~= logB(N)·n 其中前一项是一个与输入数据无关的常数,当然该项不一定小于logn
如果考虑和比较排序进行对照,基数排序的形式复杂度虽然不一定更小,但由于不进行比较,因此其基本操作的代价较小,而且在适当选择的B之下,k一般不大于logn,所以基数排序一般要快过基于比较的排序,比如快速排序。
简单来说,基数排序就是从最低位排序一直到最高位排序完成。时间复杂度O(kn) 空间复杂度O(n)
#include <iostream>
using namespace std;
int maxbit(int data[], int n)
{
int d = 1;
int max = data[0];
for (int i = 1; i < n; i++) { // 找出最大值
if (data[i] > max)
max = data[i];
}
int radix = 10;
while (max >= radix) {
d++;
radix *= 10;
}
return d;
}
void radixsort(int data[], int n) {
int d = maxbit(data, n); // 求data中最大数具有的位数,决定循环几次
int *count = new int[10]; // 10 个桶
int *tmp = new int[n];
int radix = 1;
for (int i = 0; i < d; i++) {
// 每次清零桶计数器
for (int j = 0; j < 10; j++)
count[j] = 0;
// 统计每个桶中的记录数
for (int j = 0; j < n; j++) {
int k = (data[j] / radix) % 10;
count[k]++;
}
// 分配tmp的位置
for (int j = 1; j < 10; j++) {
count[j] = count[j - 1] + count[j]; // count[j] 中 是 j 桶 和比 j 小的桶中的记录数的和
}
// 倒着来,将所有桶中记录依次收集到tmp中
for (int j = n - 1; j >= 0; j--) {
int k = (data[j] / radix) % 10;
tmp[count[k] - 1] = data[j];
count[k]--;
}
// 将临时数组复制回data
for (int j = 0; j < n; j++)
data[j] = tmp[j];
radix *= 10;
}
delete count;
delete tmp;
count = NULL;
tmp = NULL;
}
int main()
{
int data[] = { 52,353,458,279,129,152 };
radixsort(data, 6);
for (int i = 0; i < 6; i++)
cout << data[i] << " ";
}
计数排序:
计数排序对于数据范围很大的数组,需要大量时间和内存。适合排序小范围内的整数。
算法的步骤如下:
- 找出待排序的数组中最大和最小的元素
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
void count_sort(int data[], int n) {
int *tmp = new int[n];
int max = data[0]; // max为data中的最大数
int min = data[0];
for (int i = 0; i < n; i++) {
if (data[i] > max)
max = data[i];
if (data[i] < min)
min = data[i];
}
int len = max - min + 1;
int *count = new int[len];
for (int i = 0; i < len; i++)
count[i] = 0;
for (int i = 0; i < n; i++)
count[data[i]-min]++;
for (int j = 1; j < len; j++)
count[j] += count[j - 1];
for (int i = n - 1; i >= 0; i--) {
tmp[count[data[i]-min] - 1] = data[i];
count[data[i]-min]--;
}
for (int i = 0; i < n; i++)
data[i] = tmp[i];
delete count;
delete tmp;
}
桶排序
假设排序数的范围是[0,1],在[0,1]内平均划分为k个桶,将待排序数放在这些桶里。对于桶内元素个数大于 1 的进行桶内排序(常用插入排序)。因为平均划分,所以一个桶内的元素不会太多,时间复杂度O(n),点击打开维基百科-桶排序
版权声明:本文为博主原创文章,未经博主允许不得转载。