一、计数排序
1.算法思想
给定一组要排序的序列,找出这组序列中的最大值,然后开辟一个最大值加1大小的数组,将这个数组里面的元素全部置零,然后用这个数组统计出要排序的序列中各个元素出现的次数。等到统计完成的时候,排序就已经完成了。
2.算法具体步骤
(1)找出待排序序列里的最大值max;
(2)开辟一个大小为max+1的临时数组tmp ,将数组元素的所有初值赋为0;
(3)遍历待排序序列,将序列中出现值作为下标,对tmp 数组的对应位置数据进行++;
(4)上述操作完成后,tmp中统计了每个数据在待排序列中出现的次数;
(5)最后,将tmp数组里值不为0的所有下标拷进原序列(注意同一个下标可能有多个重复值,都要进行拷贝,不能遗漏),排序就算完成。
3.图解举例
4.代码实现
//计数排序
void CountSort1(int *arr, int size)
{
if (arr == NULL || size <= 0)
return;
//遍历求数组中最大的数
int max = arr[0];
for (int i = 1; i < size; ++i)
{
if (arr[i]>max)
{
max = arr[i];
}
}
//开辟一个max+1大小的空间
int* tmp = new int[max + 1];
//将tmp初始化为0
memset(tmp, 0, sizeof(max + 1));
//遍历原数组中每一个数出现的次数
for (int j = 0; j < size; j++)
{
++tmp[arr[j]];
}
int index = 0;
for (int k = 0; k < size; k++)
{
while (tmp[k]--)
{
arr[index] = k;
++index;
}
}
delete[] tmp;
}
代码优化:
void CountSort2(int* arr, size_t n)
{
//找到待排序序列的最大值和最小值
int max = arr[0];
int min = arr[0];
for (size_t i = 0; i < n - 1; ++i)
{
if (max < arr[i + 1])
{
max = arr[i + 1];
}
if (min > arr[i + 1])
{
min = arr[i + 1];
}
}
//开辟适当空间的数组
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[arr[i] - min]++;
}
//将数据拷回原数组
int index = 0;
for (int i = 0; i < range; ++i)
{
while (tmp[i]--)
{
arr[index] = i + min;
++index;
}
}
delete[]tmp;
}
5.其他
(1)时间复杂度:O(N+K),其中K主要是取决于排序数组的范围。
(2)空间复杂度:O(N),实际上,计数排序是一种以空间换时间的做法。
(3)稳定性:不稳定。
(4)使用场景:适合元素排列比较集中的情况,并且数组中出现的数字次数不止一次。
二、基数排序
1.基数排序的分类
- LSD– Least Significant Digit first(从低位向高位排)
- MSD– Most Significant Digit first(从高位向低位排)
2.算法思想
基数排序又称”桶子法”,它从低位开始将待排序的数按照这一位的值放到相应的编号为0到9的桶中。等到低位排完之后得到一个序列,再将这个序列按照次低位的大小进入相应的桶中。以此类推,直到将所有的位都排完之后,这组数就已经有序了。
3.算法的具体步骤
(1)统计待排序序列中最大的数是几位数;
(2)开辟一个与原数组大小相同的临时数组tmp;
(3)用一个count数组统计原数组中某一位(从个位向高位统计)相同的数据出现的次数。用一个start数组计算原数组中某一位(从个位向高位计算)相同的数据出现的位置。将桶中数据从小到大(由顶至底)用tmp数组收集起来;
(4)重复上述过程,直到所有位都被统计并计算过,然后用tmp收集起来;
(5)将tmp数组的数据拷回原数组,排序完成。
4.图解举例
5.代码实现
int GetMaxdigit(int* arr, int size)
{
if (arr == NULL || size <= 0)
return NULL;
int MAX = arr[0];
for (size_t i = 0; i < size; ++i)
{
if (arr[i] > MAX)
MAX = arr[i];
}
int count = 0;
while (MAX)
{
MAX /= 10;
++count;
}
return count;
}
void LSDSort(int *arr, int size)//基数排序
{
if (arr == NULL || size <= 0)
return;
int base = 1;
int digit = GetMaxdigit(arr, size);//获取最大元素的位数
int *tmp = new int[size];//开辟临时数组
while (digit--)
{
//统计某位相同的数据出现的次数
int count[10] = { 0 };
for (size_t i = 0; i < size; ++i)
{
int index = arr[i] / base % 10;
count[index]++;
}
int start[10] = { 0 };
//统计个位相同的数在数组中出现的位置
for (size_t j = 1; j < size; ++j)
{
start[j] = count[j - 1] + start[j - 1];
}
memset(tmp, 0, sizeof(int)*size);
for (int k = 0; k < size; ++k)
{
int index = arr[k] / base % 10;
tmp[start[index]++] = arr[k];
}
memcpy(arr, tmp, sizeof(int)*size);
base *= 10;
}
delete[]tmp;
}
6.其他
(1)时间复杂度:O(N*digit)
(2)空间复杂度:O(N)
(3)稳定性:稳定