桶排序和计数排序

Posted luStar

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了桶排序和计数排序相关的知识,希望对你有一定的参考价值。

突然想自己写个桶排序,然后做课后题又发现了计数排序,觉得挺有趣的。不过书上都没有给代码,所以就自己写了一下代码,超级烂0 0下面先简单介绍下这两种排序

桶排序

桶排序,就是根据散列的思想进行数据的排序。假设有M个桶,采用最简单的hash(key)=key,这样无需比较,就可以把数存入相应的桶中。针对冲突排解问题,此时查找链的方式显然不再适用,采用独立链法,把每个桶以链表的形式存储雷同元素,定义相同元素的偏序,这样能实现排序的稳定性。桶排序完全采用了简单的哈希策略,是比较容易理解的。同时,完全摒弃了CBA式的方式,从而可以突破复杂度O(nlogn)的界限。通过空间换时间的策略,可以达到O(n)的时间复杂度,代价是O(M+N)的额外空间。下面是对于整型数组的桶排序,代码很烂我也没有改,已经测试过:

 1 struct node 
 2 {
 3     node(int v = 0, node* s = NULL) :value(v), succ(s) {}
 4     int value;
 5     node* succ;
 6 };
 7 void insert(node &a, int b)//b插入到a后面
 8 {
 9     node* t = new node(b);
10     if (a.succ) t->succ = a.succ;
11     a.succ = t;
12 }
13 node* bucketSort(int* A, int n, int k)//数的范围为[0,k)
14 {
15     node* bucket = new node[k]();
16     for (int i = 0; i < n; i++)
17         insert(bucket[A[i]],A[i]);
18     return bucket;
19 }
20 void show(node a)
21 {
22     while (a.succ)
23     {
24         a = *(a.succ);//第一个节点作为哨兵不输出
25         cout << a.value << " ";
26     }
27 }
28 int main()
29 {
30     int A[15] = { 2,0,1,3,3,0,1,9,7,7,15,11,13,12,10};
31     node* p = bucketSort(A, 15, 16);
32     for (int i = 0; i < 16; i++)
33         show(p[i]);
34     system("pause");
35     return 0;
36 }

下面有测试例子,已经经过了测试,不过这里仅把排序的结果存入了一个申请空间的列表然后输出,没有进行释放的操作,也没有存入原数组或者另外一个数组0 0看具体的要求可以改动

计数排序

计数排序的基本策略,基于这样的事实:一个有序序列,元素m的秩,应当等于序列中全部元素中,小于等于m的元素数量。所以计数排序算法可以归纳如下:遍历序列,对于每个元素,再遍历整个序列,用一个额外的数组进行计数。不难看出,时间复杂度为O(n^2)。显然,无法接受这样的复杂度。

可以考虑用散列的方法来降低复杂度。同样,对于[0,k)的元素,选取M个桶,假设元素数量为n,先遍历序列,用桶数组进行计数。随后,每一个后面的桶的计数=前面桶的数量+它自身的计数。这样,每个桶的数字-1就等于桶对应元素的秩。同时,也可以处理相同元素的问题,因为当元素存在其他因素的偏序时,数字也加在了桶数组的计数中,桶数组只需要从后向前输出,就可以维持这种偏序。实现代码如下:

 1 /*计数排序*/
 2 int* countSort(int* A,int k,int n)//[0,k)范围内n个数
 3 {
 4     int* tmp = new int[k];
 5     int* s = new int[n];
 6     memset(tmp, 0, sizeof(int) * k);
 7     for (int i = 0; i < n; i++)//原始数组中的计数
 8         tmp[A[i]]++;
 9     for (int i = 0; i < k - 1; i++)//记录不大于该数的数字个数
10         tmp[i + 1] += tmp[i];
11     for (int i = n - 1; i >= 0; i--)//逆序输出
12         s[--tmp[A[i]]] = A[i];//计数哈希数组-1即为应当对应的秩,用原数组的数赋值
13     delete[] tmp;
14     return s;
15 }

代码已经经过了测试。可以看到,只需要两趟原数组遍历,一趟桶数组遍历即可完成,时间复杂度为O(M+n)。同样,辅助空间为桶数组,空间复杂度为O(M)。不难看出,计数排序适用的最合适情况,是M远小于n的时候,即数据较多而范围确不大。此时,时间复杂度仅为O(n)。

以上是关于桶排序和计数排序的主要内容,如果未能解决你的问题,请参考以下文章

八十五再探希尔排序,桶排序,计数排序和基数排序

线性排序:桶排序计数排序

线性排序:桶排序计数排序

算法计数排序桶排序和基数排序详解

算法:计数排序与桶排序

排序算法下——桶排序计数排序和基数排序