排序算法十一:计数排序 排序算法十一:计数排序
声明:引用请注明出处http://blog.csdn.net/lg1259156776/
引言
在我的博文《“主宰世界”的10种算法短评》中给出的首个算法就是高效的排序算法。本文将对排序算法做一个全面的梳理,从最简单的“冒泡”到高效的堆排序等。
系列博文的上一篇讲述了桶排序,本文讲述:计数排序(Counting sort)。
排序相关的的基本概念
-
排序:将一组杂乱无章的数据按一定的规律顺次排列起来。
- 数据表( data list): 它是待排序数据对象的有限集合。
- 排序码(key):通常数据对象有多个属性域,即多个数据成员组成,其中有一个属性域可用来区分对象,作为排序依据。该域即为排序码。每个数据表用哪个属性域作为排序码,要视具体的应用需要而定。
-
分类
- 内排序:指在排序期间数据对象全部存放在内存的排序;
- 外排序:指在排序期间全部对象个数太多,不能同时存放在内存,必须根据排序过程的要求,不断在内、外存之间移动的排序。
排序算法的分析
排序算法的稳定性
如果在对象序列中有两个对象 的相对位置不变,则称排序算法是稳定的;否则排序算法是不稳定的。
排序算法的评价
时间开销
- 排序的时间开销可用算法执行中的数据比较次数与数据移动次数来衡量。
- 算法运行时间代价的大略估算一般都按平均情况进行估算。对于那些受对象排序码序列初始排列及对象个数影响较大的,需要按最好情况和最坏情况进行估算
空间开销
算法执行时所需的附加存储。
计数排序(counting sort)
目前介绍的利用比较元素进行排序的方法对数据表长度为。但是如果知道了一些数据表的信息,那么就可以实现更为独特的排序方式,甚至是可以达到线性时间的排序。
基本思想
当数据表长度为之间,而k又比n小许多,这样可以通过统计每一个范围点上的数据频次来实现计数排序。
基本操作
根据获得的数据表的范围,分割成不同的buckets,然后直接统计数据在buckets上的频次,然后顺序遍历buckets就可以得到已经排好序的数据表。
算法的c plus plus实现
#include <iostream>
using namespace std;
void print(int a[], int sz) {
for (int i = 0; i < sz; i++ ) cout << a[i] << " ";
cout << endl;
}
void CountingSort(int arr[], int sz) {
int i, j, k;
int idx = 0;
int min, max;
min = max = arr[0];
for(i = 1; i < sz; i++) {
min = (arr[i] < min) ? arr[i] : min;
max = (arr[i] > max) ? arr[i] : max;
}
k = max - min + 1;
/* creates k buckets */
int *B = new int [k];
for(i = 0; i < k; i++) B[i] = 0;
for(i = 0; i < sz; i++) B[arr[i] - min]++;
for(i = min; i <= max; i++)
for(j = 0; j < B[i - min]; j++) arr[idx++] = i;
print(arr,sz);
delete [] B;
}
int main()
{
int a[] = {5,9,3,9,10,9,2,4,13,10};
const size_t sz = sizeof(a)/sizeof(a[0]);
print(a,sz);
cout << "----------------------
" ;
CountingSort(a, sz);
}
输出为:
5 9 3 9 10 9 2 4 13 10
----------------------
2 3 4 5 9 9 9 10 10 13
计算排序构造了k个buckets来统计数据频次,共需要两趟来实现排序,第一趟增量计数进行统计,第二趟将计数统计的对应的数重写入原始数据表中。
因为这种排序没有采用比较,所以突破了时间复杂度的上线,但是counting sort又不像是一种排序,因为在复杂数据结构中,它不能实现同结构体中排序码的排序来对结构体进行排序。也就不要提稳定与否了。
2015-9-29 艺少