线性时间排序——计数排序,基数排序,桶排序
2014-01-16 20:58
726 查看
线性时间排序即时间复杂度为Θ(n)的排序,主要有计数排序、基数排序和桶排序
以前的排序都涉及到元素比较,称为比较排序,渐近最优的为 随机快速排序(快速排序在平均情况下),merge sort归并排序和heap sort,时间复杂度为Θ(nlogn)
而这种排序都不是用比较的操作来排序,所以下届Θ(nlogn)对它们不适用
一、计数排序
时间复杂度为Θ(n+k),k为常数
计数排序适合所需排序的数组元素取值范围不大的情况,要求取值范围小于元素个数
二、基数排序
1,假设数组a所有元素i为十进制整数且位数不超过d位
2,递归对数组a的个位、十位、...、d位排序
当没位数字都界于1到k之间时,对n个d位数的每一位处理的时间为Θ(n+k),共d次处理,所以时间复杂度为Θ(dn+dk),d为常数
基数排序分LSD(Least significant digital)和MSD(Most significant digital),前者从最低位开始排,后者从最高位开始排
LSD需要保持较低位的位置,而MSD则不需要
三、桶排序
桶排序是另外一种以O(n)或者接近O(n)的复杂度排序的算法. 它假设输入的待排序元素是等可能的落在等间隔的值区间内.一个长度为N的数组使用桶排序, 需要长度为N的辅助数组. 等间隔的区间称为桶, 每个桶内落在该区间的元素. 桶排序是基数排序的一种归纳结果。
算法的主要思想: 待排序数组A[1...n]内的元素是随机分布在[0,1)区间内的的浮点数.辅助排序数组B[0....n-1]的每一个元素都连接一个链表.将A内每个元素乘以N(数组规模)取底,并以此为索引插入(插入排序)数组B的对应位置的连表中. 最后将所有的链表依次连接起来就是排序结果.
这个过程可以简单的分步如下:
设置一个定量的数组当作空桶子。
寻访序列,并且把项目一个一个放到对应的桶子去。
对每个不是空的桶子进行排序。
从不是空的桶子里把项目再放回原来的序列中。
note: 待排序元素越均匀, 桶排序的效率越高. 均匀意味着每个桶在中间过程中容纳的元素个数都差不多,不会出现特别少或者特别多的情况, 这样在排序子程序进行桶内排序的过程中会达到最优效率.(否则效率并不理想)
note: 将元素通过恰当的映射关系将元素尽量等数量的分到各个桶(值区间)里面, 这个映射关系就是桶排序算法的关键.桶的标记(数组索引Index)的大小也要和值区间有对应关系
以前的排序都涉及到元素比较,称为比较排序,渐近最优的为 随机快速排序(快速排序在平均情况下),merge sort归并排序和heap sort,时间复杂度为Θ(nlogn)
而这种排序都不是用比较的操作来排序,所以下届Θ(nlogn)对它们不适用
一、计数排序
时间复杂度为Θ(n+k),k为常数
计数排序适合所需排序的数组元素取值范围不大的情况,要求取值范围小于元素个数
#include <stdio.h> #include <stdlib.h> //计数排序 int CountSort(int* pData, int nLen) { int* pCout = NULL; //保存记数数据的指针 pCout = (int*)malloc(sizeof(int) * nLen); //申请空间 //初始化记数为0 for (int i = 0; i < nLen; ++i) { pCout[i] = 0; } //记录排序记数。在排序的值相应加一 for(int i = 0; i < nLen; ++i) { ++pCout[pData[i]]; //增 } //确定不比该位置大的数据个数 for (int i = 1; i < nLen; ++i) { pCout[i] += pCout[i - 1]; //不比他大的数据个数为他的个数加上前一个的记数。 } int* pSort = NULL; //保存排序结果的指针pSort=(int*)malloc(sizeof(int) * nLen); //申请空间 for (int i = 0; i < nLen; ++i) { //把数据放在指定位置。因为pCout[pData[i]]的值就是不比他大数据的个数 //为什么要先减一,因为pCout[pData[i]]保存的是不比他大数据的个数中包括了 //他自己,我的下标是从零开始的!所以要先减一。 --pCout[pData[i]];//因为有相同数据的可能,所以要把该位置数据个数减一。 pSort[pCout[pData[i]]] = pData[i]; } //排序结束,复制到原来数组中 for (int i = 0; i < nLen; ++i) { pData[i] = pSort[i]; } //最后要注意释放申请的空间。 free(pCout); free(pSort); return 1; } int main() { int nData[10] = {8,6,3,6,5,8,3,5,1,0}; CountSort(nData, 10); for (int i = 0; i < 10; ++i) { printf("%d ", nData[i]); } printf("\n"); system("pause"); return 0; }
二、基数排序
1,假设数组a所有元素i为十进制整数且位数不超过d位
2,递归对数组a的个位、十位、...、d位排序
当没位数字都界于1到k之间时,对n个d位数的每一位处理的时间为Θ(n+k),共d次处理,所以时间复杂度为Θ(dn+dk),d为常数
基数排序分LSD(Least significant digital)和MSD(Most significant digital),前者从最低位开始排,后者从最高位开始排
LSD需要保持较低位的位置,而MSD则不需要
#include <stdio.h> #include <stdlib.h> //计数排序,npRadix为对应的关键字序列,nMax是关键字的范围。npData是具体要 //排的数据,nLen是数据的范围,这里必须注意npIndex和npData对应的下标要一致 //也就是说npIndex[1] 所对应的值为npData[1] int RadixCountSort(int* npIndex, int nMax, int* npData, int nLen) { //这里就不用说了,计数的排序。不过这里为了是排序稳定 //在标准的方法上做了小修改。 int* pnCount = (int*)malloc(sizeof(int)* nMax); //保存计数的个数 for (int i = 0; i < nMax; ++i) { pnCount[i] = 0; } for (int i = 0; i < nLen; ++i) //初始化计数个数 { ++pnCount[npIndex[i]]; } for (int i = 1; i < 10; ++i) //确定不大于该位置的个数。 { pnCount[i] += pnCount[i - 1]; } int * pnSort = (int*)malloc(sizeof(int) * nLen); //存放零时的排序结果。 //注意:这里i是从nLen-1到0的顺序排序的,是为了使排序稳定。 for (int i = nLen - 1; i >= 0; --i) { --pnCount[npIndex[i]]; pnSort[pnCount[npIndex[i]]] = npData[i]; } for (int i = 0; i < nLen; ++i) //把排序结构输入到返回的数据中。 { npData[i] = pnSort[i]; } free(pnSort); //记得释放资源。 free(pnCount); return 1; } //基数排序 int RadixSort(int* nPData, int nLen) { //申请存放基数的空间 int* nDataRadix = (int*)malloc(sizeof(int) * nLen); int nRadixBase = 1; //初始化倍数基数为1 bool nIsOk = false; //设置完成排序为false //循环,知道排序完成 while (!nIsOk) { nIsOk = true; nRadixBase *= 10; for (int i = 0; i < nLen; ++i) { nDataRadix[i] = nPData[i] % nRadixBase; nDataRadix[i] /= nRadixBase / 10; if (nDataRadix[i] > 0) { nIsOk = false; } } if (nIsOk) //如果所有的基数都为0,认为排序完成,就是已经判断到最高位了。 { break; } RadixCountSort(nDataRadix, 10, nPData, nLen); } free(nDataRadix); return 1; } int main() { //测试基数排序。 int nData[10] = {123,5264,9513,854,9639,1985,159,3654,8521,8888}; RadixSort(nData, 10); for (int i = 0; i < 10; ++i) { printf("%d ", nData[i]); } printf("\n"); system("pause"); return 0; }
三、桶排序
桶排序是另外一种以O(n)或者接近O(n)的复杂度排序的算法. 它假设输入的待排序元素是等可能的落在等间隔的值区间内.一个长度为N的数组使用桶排序, 需要长度为N的辅助数组. 等间隔的区间称为桶, 每个桶内落在该区间的元素. 桶排序是基数排序的一种归纳结果。
算法的主要思想: 待排序数组A[1...n]内的元素是随机分布在[0,1)区间内的的浮点数.辅助排序数组B[0....n-1]的每一个元素都连接一个链表.将A内每个元素乘以N(数组规模)取底,并以此为索引插入(插入排序)数组B的对应位置的连表中. 最后将所有的链表依次连接起来就是排序结果.
这个过程可以简单的分步如下:
设置一个定量的数组当作空桶子。
寻访序列,并且把项目一个一个放到对应的桶子去。
对每个不是空的桶子进行排序。
从不是空的桶子里把项目再放回原来的序列中。
note: 待排序元素越均匀, 桶排序的效率越高. 均匀意味着每个桶在中间过程中容纳的元素个数都差不多,不会出现特别少或者特别多的情况, 这样在排序子程序进行桶内排序的过程中会达到最优效率.(否则效率并不理想)
note: 将元素通过恰当的映射关系将元素尽量等数量的分到各个桶(值区间)里面, 这个映射关系就是桶排序算法的关键.桶的标记(数组索引Index)的大小也要和值区间有对应关系
相关文章推荐
- 【算法导论】第8章线性时间排序_计数排序、基数排序、桶排序
- 线性时间排序:计数排序、基数排序、桶排序
- 线性时间排序C++代码集合(计数排序,基数排序,桶排序([0~1)的浮点数))
- 线性时间排序--计数排序、基数排序、桶排序
- 计数排序,基数排序,桶排序和快速排序算法时间对比
- [算法学习]线性时间排序:计数排序、基数排序和桶排序
- 线性排序算法-- 计数排序,基数排序,桶排序
- 线性时间排序之计数排序,基数排序和桶排序
- 线性排序----计数排序, 基数排序, 桶排序
- 线性排序之基数排序,桶排序,计数排序
- 【算法学习】线性时间排序-计数排序、基数排序和桶排序详解与编程实现
- 各种线性排序记录(计数排序,基数排序,桶排序) 及 希尔排序
- 基数排序--基于计数排序的线性时间复杂度的排序算法
- 【算法学习】线性时间排序-计数排序、基数排序和桶排序详解与编程实现
- 【算法学习】线性时间排序-计数排序、基数排序和桶排序详解与编程实现
- 线性时间排序:计数排序、计数排序、桶排序
- 【算法导论】线性时间排序-计数排序、基数排序及桶排序
- 计数排序 (线性时间排序之基数排序,计数排序及java实现)
- 易解排序算法 - 空间换取时间(java写:基数排序,计数排序,桶排序,排序全部源代码)
- 三种线性排序算法 计数排序、桶排序与基数排序