线性时间排序--计数排序、基数排序、桶排序
来源:互联网 发布:软件技术支持面试题 编辑:程序博客网 时间:2024/05/16 12:33
之前总结的都是通过比较方法进行排序的算法,我们知道,通过比较排序算法平均时间复杂度最多为O(lgN)。
这篇文章来分析一下非比较的线性时间排序方法,计数排序,基数排序,桶排序。
一:计数排序
正如它的名字,计数排序是通过计算待排序元素小于等于该元素的次数这个属性,然后利用该属性将元素以次数为下标挪入另外的数组,不能原地挪动,就得到了排序后的结果。
代码如下:
void counting_sort(int* arr, int* result, const int size, const int k){ int i = 0; vector<int> count(k+1, 0); for(i=0; i<size; ++i) ++count[arr[i]]; for(i=1; i<=k; ++i) count[i] += count[i-1]; for(i=size-1; i>=0; --i){ result[count[arr[i]]-1] = arr[i]; //注意下标-1 --count[arr[i]]; } }
时间复杂度为O(n),空间复杂度,O(n+k)
注意:
>>>1.计数排序一共使用了两个额外数组。一个用来存放结果即result数组,一个用来计数count数组。result数组是用来存放结果的,自然它的大小和原数组arr的大小是完全一致的。而count数组使用来对每个元素计数的,它的下标就是原数组中的元素,所以它的大小由原数组中最大值决定,大小是最大值max+1,这里我们通过外部参数传入,它就是k。这么做是因为它需要支持最大值的计数。
>>>2.每次计数前需初始化,这是所有counter共同遵循的特征。上面代码中vector已经初始化过了。
>>>3.对每一个原数组元素,它现在是count数组的下标,所以count[arr[i]]表示它出现的次数,可能是重复出现的。
>>>4.我们需要将count数组中内容一个一个累加起来,每一个当前坐标对应的值,表示小于等于它的坐标(原数组值)的元素数目。
>>>5.倒着for循环是为了保证排序的稳定性。因为我们倒着回去,并且会将count[arr[i]减一,正好是后面arr[i]对应未减一前的count值,所以后面的元素由于count值大会被放在后面。
>>>6.千万要注意赋给result时下标要减1,因为count数组中元素的最小值是1,不可能某个arr[i]元素在count中出现了0次。result的下标最小值为0,所以需要减1。否则会越界。
int main(){ int array[] = {3, 9, 12, 7, 6, 15, 4, 3, 2, 1}; int len = sizeof(array) / sizeof(len); int result[len]; std::fill(result, result+len, 0); counting_sort(array, result, len, 15); for(auto i : result) cout<<i<<' '; cout<<endl; return 0;}
二:基数排序
基本思想:
将待排序数组中的每组关键字依次进行桶分配。比如对三位数排序,我们先分配十个桶,分别装数字0~9。我们先按个位吧所有数放入相应的桶中,然后按桶序0~9在吧它们输出。然后按十位把他们再次放入桶中,按桶序再次输出。重复多次,得到就是有序数组。
我们可以利用队列数组来轻易实现基数排序的桶集合。每次push进去,然后按桶下标在pop出来,重复操作就完成排序。
看一道题:
对于一个int数组,请编写一个基数排序算法,对数组元素排序。
给定一个int数组A及数组的大小n,请返回排序后的数组。保证元素均小于等于2000。
测试样例:
[1,2,3,5,2,3],6
[1,2,2,3,3,5]
代码如下:
class RadixSort {public: int* radixSort(int* A, int n) { queue<int> bucket[10]; for(int k=1; k<=1000; k*=10){ //d限制 for(int i=0; i<n; ++i) //分配 bucket[A[i]/k%10].push(A[i]); int index = 0; for(int i=0; i<10; ++i){ //收集 while(!bucket[i].empty()){ A[index++] = bucket[i].front(); bucket[i].pop(); } } } return A; }};它的时间复杂度为O(d(n+radix)),radix是基数,其中,一趟分配时间复杂度为O(n),一趟收集时间复杂度为O(radix),共进行d趟分配和收集。 空间效率:O(rd+n)约等于O(n)。
稳定性:稳定。
三:桶排序
假设有一组长度为N的待排关键字序列K[1....n]。首先将这个序列划分成M个的子区间(桶) 。然后基于某种映射函数 ,将待排序列的关键字k映射到第i个桶中(即桶数组B的下标 i) ,那么该关键字k就作为B[i]中的元素(每个桶B[i]都是一组大小为N/M的序列)。接着对每个桶B[i]中的所有元素进行比较排序(可以使用快排)。然后依次枚举输出B[0]....B[M]中的全部内容即是一个有序序列。
假如待排序列K= {49、 38 、 35、 97 、 76、 73 、 27、 49 }。这些数据全部在1—100之间。因此我们定制10个桶,然后确定映射函数f(k)=k/10。则第一个关键字49将定位到第4个桶中(49/10=4)。依次将所有关键字全部堆入桶中,并在每个非空的桶中进行快速排序。
假如待排序列K= {49、 38 、 35、 97 、 76、 73 、 27、 49 }。这些数据全部在1—100之间。因此我们定制10个桶,然后确定映射函数f(k)=k/10。则第一个关键字49将定位到第4个桶中(49/10=4)。依次将所有关键字全部堆入桶中,并在每个非空的桶中进行快速排序。
有一种说法桶排序不是一种实现,而是一种思想,比如基数排序就是用了bucket,我认为它也可以算作一种桶排序。
0 0
- 五 线性时间排序(基数排序、计数排序和桶排序)
- 线性时间排序——计数排序,基数排序,桶排序
- 算法导论-- 线性时间排序(计数排序、基数排序、桶排序)
- 线性时间排序之计数排序,基数排序和桶排序
- 线性时间排序(计数排序/桶排序/基数排序)
- 线性时间排序--计数排序、基数排序、桶排序
- 线性时间排序(计数排序、基数排序、桶排序)
- 线性时间排序:计数排序、基数排序、桶排序
- 线性排序----计数排序, 基数排序, 桶排序
- 【算法学习】线性时间排序-计数排序、基数排序和桶排序详解与编程实现
- 【算法学习】线性时间排序-计数排序、基数排序和桶排序详解与编程实现
- MIT:算法导论——5.线性时间排序:计数排序、基数排序以及桶排序
- 数据结构与算法——线性时间排序(计数排序、基数排序、桶排序)
- 算法导论 第8章 线性时间排序(计数排序、基数排序、桶排序)
- [算法学习]线性时间排序:计数排序、基数排序和桶排序
- 线性排序之基数排序,桶排序,计数排序
- 计数排序 (线性时间排序之基数排序,计数排序及java实现)
- 基数排序 计数排序 桶排序
- 链表的重要总结 系列四
- JVM 内存管理
- 解决安装SVN时报2503,2502的问题
- 儲存格式理解筆記(一)WAV格式分析2,理解含意。
- 单链表的操作
- 线性时间排序--计数排序、基数排序、桶排序
- 【诗】《丧钟为谁而鸣》
- Homebrew套件软件管理-ps入职美团第一天
- 防火墙
- 系统应用 第一章课后5
- Visual Assist X 非常实用的Rrfactor功能
- Ubuntu 16.04 一系列软件安装命令,包括QQ、搜狗、Chrome、vlc、网易云音乐安装方法
- gui - 图形用户界面
- mq - 消息队列