计数排序
计数排序顾名思义,要计算数值。我们先来看一道题
实现一个算法,确定一个字符串 s 的所有字符是否全都不同。
这道题就会体现到计数思路。想要查明是不是都相同,我们可以先遍历一遍,记录每一个字符出现的次数,如果有出现过两次的,那就不是相同。但是题为字符串,应该怎么记录?次数是一个整形数值,我们先创建一个整形数组,然后开始遍历字符串。如果用 i 来做下标,出现同样的字符应该怎样判断,毕竟相同字符出现的位置是随机的,所以我们要让所有字符都能有自己唯一的下标。这里我找的就是字符的ANSCII码值,每个字符减去字符1,得到自己独有的下标,然后这个下标的值+1即可。当然数组得提前初始化为0。
bool isUnique(char* astr){
int ptr[100] = {0};
int i = 0;
int j = 0;
int len = strlen(astr);
for(i = 0; i < len; i++)
{
j = astr[i] - '1';
ptr[j] += 1;
if(ptr[j] != 1)
{
return false;
}
}
return true;
}
回到计数排序,我们要是想办法让所有的数据都有自己的唯一。思路确定后,我们直接看代码。
代码如下:
//时间O(N + range)
//空间O(N + range)
//适合范围集中的数据,只适合整形
void CountSort(int* a, int n)
{
int max = a[0];
int min = a[0];
int i = 0;
for (i = 1; i < n; i++)
{
if (a[i] > max)
{
max = a[i];
}
if (a[i] < min)
{
min = a[i];
}
}
int range = max - min + 1;
int* count = (int*)calloc(range, sizeof(int));
if (count == NULL)
{
perror("malloc fail\n");
exit(-1);
}
//统计次数
for (i = 0; i < n; i++)
{
count[a[i] - min]++;
}
//排序
int k = 0;
int j = 0;
for (j = 0; j < range; j++)
{
while (count[j])
{
a[k++] = j + min;
}
}
free(count);
}
可以看到这里是先找到最大最小值,然后利用这两个值来确定每个值特定的值。
基数排序
基数排序的思路是找关键字,根据关键字来一次次缩短排序范围。基数排序有两种方法,MSD和LSD,不过不重要,只要记住它是根据关键字来排序就好。MSD是从最高位关键字开始排序,将序列分成若干子序列,对每个子序列按照次一位的关键字进行排序,再将序列分成若干子序列,重复这个操作,直到最次位的关键字;LSD正好相反,从最次位的关键字开始排序,排到最高位的关键字。
实际代码中和MSD LSD有不同。
现在有一组数据
278,109,063,930,589,184,505,269,008,083
对于这组数据,可以这样排序,列出来 0 到 9 的数字,根据每个数的个位数来放置,比如278放在8这个位置,这样一次排下来后,9这个位置有109 589 269三个位置,也有其他的位置有多个数,排完一次,按照 0 到 9 的顺序,把这些数据再放回去,然后根据十位数,再放置一次所有的数据,最后根据百位数再放置一遍,拿回来后数据就排好了。
按照这个思路,我们需要队列,需要有分发和回收数据的函数。下面代码中用到了C++的queue库,用C语言写的就需要自己手动实现队列,替换掉C++语句。
//基数排序
#define K 3 //关键字个数
#define RADIX 10 //基数的个数
queue<int> q[RADIX];
void PrintArray(int* a, int n)
{
for (int i = 0; i < n; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
}
int GetKey(int val, int k)//根据第几次取关键字,得到对应的值
{
int key = 0;
while (k >= 0)
{
key = val % 10;
val /= 10;
k--;
}
return key;
}
void Distribute(int a[], int left, int right, int k)
{
for (int i = left; i < right; ++i)
{
int key = GetKey(a[i], k);
q[key].push(a[i]);
}
}
void Collect(int a[])
{
int k = 0;
for (int i = 0; i < RADIX; ++i)
{
while (!q[i].empty())
{
a[k++] = q[i].front();
q[i].pop();
}
}
}
void RadixSort(int a[], int left, int right)//[left, right]
{
for (int i = 0; i < K; ++i)
{
//分发数据
Distribute(a, left, right, i);
//回收数据
Collect(a);
}
}
int main()
{
int arr[] = {278, 109, 63, 930, 589, 184, 505, 269, 8, 83};
int n = sizeof(arr) / sizeof(arr[0]);
PrintArray(arr, n);
RadixSort(arr, 0, n - 1);
PrintArray(arr, n);
return 0;
}
结束。