计数排序
计数排序的基本思想:
给一个需要排序的数组,选出数组中的最大值和最小值确定这个数组的范围,新开辟一个这个范围大小的数组空间,这个临时数组中存储的是原来数组中各个元素出现的次数,最后再根据元素出现的次数将元素拷回原数组,此时,原数组有序。
代码实现:
//计数排序
void CountSort(int* a, int n)
{
assert(a);
int max = a[0];
int min = a[0];
for (int i = 0; i < n; ++i)
{
if (a[i] > max)
max = a[i];
if (a[i] < min)
min = a[i];
}
int range = max - min + 1;
int* tmp = new int[range];
memset(tmp, 0, sizeof(int)* range);
for (size_t i = 0; i < n; ++i)
{
tmp[a[i] - min]++;
}
int j = 0;
for (int i = 0; i < range; ++i)
{
while (tmp[i]--)
{
a[j] = i + min;
j++;
}
}
delete[] tmp;
}
计数排序是一种非比较的排序方法,它的时间复杂度是O(N+K),空间复杂度是0(K),其中K是要排序的数组的范围。可以看出计数排序是一种以空间呢换取时间的方法。如果当K>N*logN的时候,计数排序就不是好的选择了,因为基于比较排序的算法的下限是O(N*logN)。所以计数排序比较适合数据范围集中的排序,且不能是字符串。
基数排序
基数排序的基本思想:
LSD– Least Significant Digit first(从低位向高位排)
MSD– Most Significant Digit first(从高位向低位排)
基数排序又称“桶子法”,是一种基于元素各个位上的数字进行的排序,先从个位开始,分别准备两个元素为0-9的桶子,第一个桶用来记录数组中各个元素个位上的数字在i(i = 0…..9)号位置出现的次数,第二个桶子用来记录该位置上对应的数据第一次出现的位置,然后以此类推往高位上走。这样的叙述似乎是有些难以理解,同样用图解释:
假设有如下数组,先对个位排序:
将对个位排完序的结果拷给原来的数组a,再对十位进行排序:
代码实现:
//基数排序
void LSDSort(int* a, int n)
{
assert(a);
//base总比digits多一个数量级
int base = 10;
int digits = 1;
for (int i = 0; i < n; ++i)
{
while (a[i] >= base)
{
digits++;
base *= 10;
}
}
base = 1;
//临时数组
int* buckets = new int[n];
for (int j = 0; j < digits; ++j)
{
//第一个桶
int count[10] = { 0 };
for (size_t i = 0; i < n; ++i)
{
int num = (a[i] / base) % 10;
count[num]++;
}
//第二个桶
int start[10] = { 0 };
for (int i = 1; i <= 10; ++i)
{
start[i] = start[i - 1] + count[i - 1];
}
//排序
for (int i = 0; i < n; ++i)
{
int num = (a[i] / base) % 10;
int index = start[num]++;
buckets[index] = a[i];
}
//拷回a
memcpy(a, buckets, sizeof(int)*n);
base *= 10;
}
delete[]buckets;
}
基数排序是一种非比较排序,它的时间复杂度是O(N*digit),其中digit是这组待排序中的最大的数的数量级。基数排序的空间复杂度就是在分配元素时,使用的桶空间,所以基数排序的空间复杂度是O(N)。它是一种稳定的排序方法。