排序按照思想可以分为比较排序和非比较排序,比较排序共有7种,非比较排序有两种,比较排序在之前的博客中已经讨论过了,所以此博客主要来研究下基数和计数排序。
1、计数排序1
1.1思想
计数排序的思想是将数值作为数组的下标,每次将数组的值加1,之后遍历数组,数组中不为0的值的下标即是排序好的数值。
技术排序必须要一块辅助空间,为了节省空间,我们一般开辟max-min大小的空间。(max和min为数值中的最大和最小值)。
1.2 复杂度
时间复杂度O(N)
空间复杂度O(max-min)
1.3适用场景
所排数值较为集中,数值个数较少。计数排序是一种稳定性的排序方法。
1.4具体代码
void CountSort(int* arr, int size) //计数排序
{
int max = arr[0];
int min = arr[0];
int index = 1;
while (index < size) //求数值的最大最小值
{
if (arr[index] > max)
{
max = arr[index];
}
if (arr[index] < min)
{
min = arr[index];
}
++index;
}
size_t tmpsize = max - min + 1;
int* tmp = new int[tmpsize];
memset(tmp, 0, tmpsize* sizeof(int));
index = 0;
while (index < size)
{
++tmp[arr[index++] - min];
}
index = 0;
int count = 0;
while (index < tmpsize)
{
while (tmp[index] != 0)
{
arr[count++] = index + min; //若保存的值不为0,则排序好的值为下标+min
--tmp[index];
}
++index;
}
delete[] tmp;
}
{
int max = arr[0];
int min = arr[0];
int index = 1;
while (index < size) //求数值的最大最小值
{
if (arr[index] > max)
{
max = arr[index];
}
if (arr[index] < min)
{
min = arr[index];
}
++index;
}
size_t tmpsize = max - min + 1;
int* tmp = new int[tmpsize];
memset(tmp, 0, tmpsize* sizeof(int));
index = 0;
while (index < size)
{
++tmp[arr[index++] - min];
}
index = 0;
int count = 0;
while (index < tmpsize)
{
while (tmp[index] != 0)
{
arr[count++] = index + min; //若保存的值不为0,则排序好的值为下标+min
--tmp[index];
}
++index;
}
delete[] tmp;
}
2、基数排序
2.1思想
最高位优先(Most Significant Digit first)法,简称MSD法:先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。
最低位优先(Least Significant Digit first)法,简称LSD法:先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。
开辟一个和所排数组一样大小的空间,对数组中的值按照个位大小放到不同的桶中,之后将桶中的值倒回到数组中,在对十位进行类似的排序,直到排到数组中的最大位数停止,此时数组中的值已经有序。
2.2实现过程
73 22 93 43 55 14 28 65 39 81
首先根据个位数的数值,在遍历数据时将他们分配到编号0至9的桶(个位数值与桶号一一对应)中。
分配结果如下图显示:
0 |
|
|
|
|
1 | 81 |
|
|
|
2 | 22 |
|
|
|
3 | 73 | 93 | 43 |
|
4 | 14 |
|
|
|
5 | 55 | 65 |
|
|
6 |
|
|
|
|
7 |
|
|
|
|
8 | 28 |
|
|
|
9 |
|
|
|
|
分配结束后,将桶中所盛的数据按照桶号由小到大依次重新串起来放回到原来的数组中,此时数组中的值为:
81 22 73 93 43 14 55 65 28 39
接着再以十位数值来进行分配(分配原理同上),分配结果如下:
0 |
|
|
|
|
1 | 14 |
|
|
|
2 | 22 | 28 |
|
|
3 | 39 |
|
|
|
4 | 43 |
|
|
|
5 | 55 |
|
|
|
6 | 65 |
|
|
|
7 | 73 |
|
|
|
8 | 81 |
|
|
|
9 | 93 |
|
|
|
此时将桶中的数据依次放回到原来的数组中去,得到如下的序列:
22 28 39 43 55 65 71 81 93
此时我们发现数组中的值已经有序。
1.3复杂度
空间复杂度O(N)
时间复杂度O(N*digit)(digit为数组中最大的位数)
1.4适用场景
基数排序是一种稳定的排序方法,在所排数值位数较低时效率较高。
1.5代码实现(c++)
void LSDSort(int *arr, int size) //基数排序
{
assert(arr);
int count[10] = { 0 };//统计相同位的出现个数
int start[10] = { 0 };//保存每一位起始位置
int* bucket = new int[size];//桶
int digit = GetMaxDigit(arr, size);
for (int i = 0; i < size; ++i)
{
int num = arr[i] % 10;
++count[num];
}
for (int i = 1; i < size; ++i)
{
start[i] = count[i] + start[i -1];
}
int baseDigit = 1;
while (digit--)
{
for (int i = 0; i < size; ++i)
{
int num = arr[i] / baseDigit % 10;
int &index = start[num];
bucket[index++] = arr[i];
}
baseDigit *= 10;
memcpy(arr, bucket, size*sizeof(int));
}
delete[] bucket;
}
int GetMaxDigit(int* arr, int size)//获取数组中最高的位数
{
assert(arr);
int max = 10;
int digit = 1;
int index = 0;
while (index < size)
{
if (arr[index] > max)
{
++digit;
max *= 10;
}
++index;
}
return digit;
}
{
assert(arr);
int count[10] = { 0 };//统计相同位的出现个数
int start[10] = { 0 };//保存每一位起始位置
int* bucket = new int[size];//桶
int digit = GetMaxDigit(arr, size);
for (int i = 0; i < size; ++i)
{
int num = arr[i] % 10;
++count[num];
}
for (int i = 1; i < size; ++i)
{
start[i] = count[i] + start[i -1];
}
int baseDigit = 1;
while (digit--)
{
for (int i = 0; i < size; ++i)
{
int num = arr[i] / baseDigit % 10;
int &index = start[num];
bucket[index++] = arr[i];
}
baseDigit *= 10;
memcpy(arr, bucket, size*sizeof(int));
}
delete[] bucket;
}
int GetMaxDigit(int* arr, int size)//获取数组中最高的位数
{
assert(arr);
int max = 10;
int digit = 1;
int index = 0;
while (index < size)
{
if (arr[index] > max)
{
++digit;
max *= 10;
}
++index;
}
return digit;
}