有关比较排序的算法的介绍,在这里:
http://zhweizhi.blog.51cto.com/10800691/1786367
——————————————————————————————————
这里主要介绍非比较排序算法。非比较排序主要有2种算法,分别是:
计数排序(CountSort)
基数排序(RadixSort)
一、计数排序
计数排序的实现需要借助哈希表。
思路比较清晰,注释也比较详细
这种排序的套路其实就是把每个元素映射到一个哈希表上,
然后这个哈希表自然就是排好的顺序了。
只不过有些细节要注意,
比如对于哈希表的长度应该怎么开辟合适
比如对于哈希表中为0的元素怎么处理
比如对于多个重复元素怎么处理 等……
以上细节都在代码里有比较详细的注释
下面是代码:
//非比较排序——计数排序 void CountSort(int *arr, int sz, int min, int max) { assert(arr); assert(min < max); if (min == max) { return; } //开辟哈希表 和 临时数组(这个临时数组可以存放排序好的元素,最后将该数组的内容赋给arr即可) int *TmpList = new int[sz]; int *CountList = new int[max - min + 1]; memset(CountList, 0, (max - min + 1) * sizeof(int) ); //哈希表元素初始化为全0 for (int i = 0; i < sz; ++i) { ++CountList[arr[i] - min]; } //向临时数组中存放排序好的元素 int index = 0; for (int i = 0; i < max - min + 1; ++i) { while (CountList[i]-- != 0) { TmpList[index++] = i + min; } } //将临时数组中的内容导入到arr中 for (int i = 0; i < sz; ++i) { arr[i] = TmpList[i]; } delete[] TmpList; delete[] CountList; }
二、基数排序
基数排序,用到了“哈希桶”的思想。
它的实现思路是利用哈希桶按位排序 这里可以是从低位到高位,也可以是从高位到低位
所以根据按位排序的顺序,又可以分为:
低位基数排序(LSD)
高位基数排序(MSD)
这里可以注意,每次排序对象的范围都是 0 ~ 9 这10个元素。
下面用代码来实现低位基数排序:
//—————非比较排序——基数排序: //找出一组数据中的最长位 int MaxDigit(int *arr, int sz) { int max = 10; int digit = 1; for (int i = 0; i < sz; ++i) { while (arr[i] > max) { ++digit; max *= 10; } } return digit; } //基数排序 void LSDSort(int *arr, int sz) { assert(arr); if (sz <= 1) { return; } int StartList[10] = { 0 }; int CountList[10] = { 0 }; int *Backet = new int[sz]; int Digit = MaxDigit(arr, sz); //数组中的最长元素的位数 int BaseDigit = 1; for (int i = 1; i <= Digit; ++i) { //装入桶中 memset(CountList, 0, sizeof(int) * 10); for (int i = 0; i < sz; ++i) { int num = (arr[i] / BaseDigit) % 10; ++CountList[num]; } //整理“桶” StartList[0] = 0; for (int i = 1; i < 10; ++i) { StartList[i] = CountList[i - 1] + StartList[i - 1]; } //对桶中盛放的元素进行排序(方法类似计数排序) for (int i = 0; i < sz; ++i) { int num = (arr[i] / BaseDigit) % 10; int &index = StartList[num]; Backet[index++] = arr[i]; } memcpy(arr, Backet, sizeof(int) * sz); BaseDigit *= 10; } delete[] Backet; }
这里举个例子来说明:(画图偷懒用的是Excel)
有一组待排序的数据:
11, 33, 51, 72, 84, 91
排序的过程大体上是这样的:
先排个位:
将所有元素按个位的大小,映射到一个桶中:
然后按照桶号的顺序,将桶中的元素“串起来”,得到元素序列:
11 51 91 72 33 84
再排十位:
将上一步最后得到的数组的所有元素按十位的大小,映射到一个桶中:
看,排好了。
三、总结
这里介绍了两种非比较排序:计数排序、基数排序。
其中,计数排序的时间复杂度是O(N),空间复杂度是O(MAX - MIN),可以看出,对于一组范围比较小的数据,它的速度非常快,而且稳定(比较排序中时间复杂度平均最快也是O(N * LgN)),但如果数据范围跨度过大,那么这种方式会浪费很多空间。
基数排序是计数排序的升级版,用桶来代替计数排序中的辅助数组,时间复杂度为O(N * 位数) 空间复杂度为O(N)。它每趟排序都按照当前位上数据的大小对数组进行排序,而这又分为LSD和MSD两种(分别是从低位到高位;和从高位到低位)
非比较排序的稳定性好,速度快。