在最坏情况下,任何比较排序算法都需要做O(nlgn)次比较。
然而,在指定的条件下,线性时间的排序算法可以使得排序在O(n)时间内完成。
计数排序
假设n个输入元素中的每一个都是0到k区间内的一个整数,其中k为某个整数。k=O(n)时,排序运行时间为O(n)。
主要思想:
创建长度为k的数组C,将对应的输入数组A的值作为索引来统计k数组每个下标出现的次数。
代码如下:
void Counting_Sort(int* intArr,int* outArr,int k,int len) { int* cArr = new int[k]; memset(cArr,0,k*sizeof(int)); // 用cArr存储各个值出现次数 for (int i=0; i<len; ++i) { cArr[intArr[i]] +=1; } // 存储当前值存在的话,所在的大小位数 for (int i=1; i<k; ++i) { cArr[i]=cArr[i]+cArr[i-1]; } for (int i=len-1; i>=0; --i) { outArr[cArr[intArr[i]]-1] =intArr[i]; cArr[intArr[i]]-=1; } delete[] cArr; }
基数排序
假设对n个d位整数进行排序,从低到高位排,需要进行d轮排序。因为每位取值0~9,所以需要d次 k为9的排序方法计数排序。
因而基数排序总时间为O(d(n+k))。
代码如下:
typedef struct _baseVar { int idx; int var; }BaseVar; // 调整后的计数排序 void Counting_Sort_Ex(BaseVar* intArr, BaseVar* outArr, int k, int len) { int* cArr = new int[k]; memset(cArr, 0, k*sizeof(int)); // 用cArr存储各个值出现次数 for (int i = 0; i<len; ++i) { cArr[intArr[i].var] += 1; } // 存储当前值存在的话,所在的大小位数 for (int i = 1; i<k; ++i) { cArr[i] = cArr[i] + cArr[i - 1]; } for (int i = len - 1; i >= 0; --i) { int nIdx = cArr[intArr[i].var]-1; outArr[nIdx].var = intArr[i].var; outArr[nIdx].idx = intArr[i].idx; cArr[intArr[i].var] -= 1; } delete[] cArr; } // void Radix_Sort(int* intArr, int d, int len) { BaseVar* varArr = new BaseVar[len]; BaseVar* outArr = new BaseVar[len]; // d位循环 int div_var = 1; for (int j = 0; j<d; ++j) { for (int i = 0; i<len; ++i) { varArr[i].var = (intArr[i] % (div_var * 10)) / div_var; //取每个位的值 varArr[i].idx = i; //记录排序前的索引 } // 计数排序 Counting_Sort_Ex(varArr, outArr, 10, len); for (int i = 0; i<len; ++i) { varArr[i].var = intArr[outArr[i].idx]; //根据新索引赋值原来索引上的值 } // 对应将原数组排序 for (int i = 0; i<len; ++i) { intArr[i] = varArr[i].var; } div_var *= 10; } delete[] varArr; delete[] outArr; }
桶排序
假设输入数据服从均匀分布,平均情况下它的时间代价为O(n)。
主要思想:
对数据划分成n个大小相同的区间,称为桶。然后,将个输入按照映射函数f(k)尽量均匀分布到各个桶中。最后先分别对每个桶进行排序,
然后遍历每个桶,并按照次序把每个桶中元素列出来即可。
代码略。