在此之前我是不了解基数排序的, 昨天专门研究了下。 感觉是一种非常不错的排序算法。
时间复杂度很低o(n), 空间复杂度为o(n)。
说实话,没有多少种排序算法可以达到这样的性能。
仔细研究了下,发现在很多种情况下采用这样算法可以做到很好的处理。
(例如字符串排序, 电话号码排序等)
一、计数排序
如果想要学会基数排序, 那么首先要了解计数排序, 因为基数排序封装了计数排序。
那么对于计数排序, 我有一种很强烈的感觉就是用空间换取时间。
我想大家都知道哈希思想。
1、开辟一块很大的辅助空间(能够存储最大值), 辅助空间中的下标对应元素本身,
2、下标对应的值是该元素出现的个数。
3、最后遍历辅助空间, 累计出现的次数然后存储起来就排序成功。
非常推荐大家看这篇文章: 计数排序
Code
//版本一
vector<int> CountSort(int* arr, int len) {
if (!arr || len <= 0) {
return vector<int>();
}
int max = arr[0];
//使得max获取最大值
for (int i = 1; i < len; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
//创建辅助数组
vector<int> tmp_arr(max + 1, 0);
//累次出现次数
for (int i = 0; i < len; i++) {
tmp_arr[arr[i]]++;
}
//遍历辅助数组, 存储在ret_arr中返回
vector<int> ret_arr(len);
int index = 0;
for (int i = 0; i < tmp_arr.size(); i++) {
while (tmp_arr[i]) {
ret_arr[index++] = i;
tmp_arr[i]--; //计数器--
}
}
return ret_arr;
}
这个算法我们可以发现
1、首先获取序列中的max值。 时间复杂度为o(n)
2、开辟辅助数组空间, 空间复杂度为s(max)
3、最后遍历辅助数组, 时间复杂度为o(max),
4、还开辟了s(n)的数组, 存储遍历后有序的序列。
综上所述:时间复杂度:o(n+max); 空间复杂度:s(max+n)
我们可以发现优化核心是max值, 如果序列中只有5个元素, 但是有一个值很大, 我们都会开辟max大数组空间, 空间利用率很低。
[100, 102, 105, 107, 110] 最大值为110, 开辟从0到110大小空间。
优化一
//版本二
vector<int> CountSort1(int* arr, int len) {
if (!arr || len <= 0) {
return vector<int>();
}
int min = arr[0], max = arr[0];
//1、获取最大值和最小值
for (int i = 0; i < len; i++) {
if (max < arr[i]) {
max = arr[i];
}
if (min > arr[i]) {
min = arr[i];
}
}
//2、开辟辅助空间 - 统计次数
vector<int> tmp_arr(max - min + 1, 0);
for (int i = 0; i < len; i++) {
tmp_arr[arr[i] - min]++;
}
//3、遍历辅助空间, 获取有序序列
vector<int> ret_arr(len, 0);
int index = 0;
for