桶排序算法
概念:
桶排序(Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶里。每个桶再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序),最后依次把各个桶中的记录列出来记得到有序序列。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是比较排序,他不受到O(n log n)下限的影响。
基本思想
桶排序的思想近乎彻底的分治思想。
桶排序假设待排序的一组数均匀独立的分布在一个范围中,并将这一范围划分成几个子范围(桶)。
然后基于某种映射函数f ,将待排序列的关键字 k 映射到第i个桶中 (即桶数组B 的下标i) ,那么该关键字k 就作为 B[i]中的元素 (每个桶B[i]都是一组大小为N/M 的序列 )。
接着将各个桶中的数据有序的合并起来 : 对每个桶B[i] 中的所有元素进行比较排序 (可以使用快排)。然后依次枚举输出 B[0]….B[M] 中的全部内容即是一个有序序列。
思路实现:
1 设置一个定量的数组当作空桶。
2 寻访序列,并且把项目一个一个放到对应的桶子去。
3 对每个不是空的桶子进行排序。
4 从不是空的桶子里把项目再放回原来的序列中。
桶排序的简单实现(C++):
void bucketSort(vector<int>&arr){ if(arr.size()<2){ return; } int maxValue = INT_MIN; for (int i=0; i<arr.size(); i++) { maxValue = max(maxValue,arr[i]); } int *bucket=new int[maxValue+1]; for(int i=0;i<maxValue+1;i++){ bucket[i]=0; } for(int i=0;i<arr.size();i++){ bucket[arr[i]]++;//相同的数分到同一个桶 } int i=0; for (int j=0; j<maxValue+1; j++) { while (bucket[j]-- > 0) { arr[i++]= j; } } delete [] bucket; }
算法题目:
给定一个数组,求排序后,相邻两数的最大差值,要求时间复杂度为O(n),且要求不能用非基于比较的排序。
思路:遍历数组,找到最小值和最大值。如果最小值和最大值相等,则返回0. 假设数组长度为 N ,我们准备一个长度为 N+1 的数组,将最小值放在数组索引位置为 0 的位置上,最大值放在索引位置为 N 的位置上。中间的数字就放在中间的位置里。
比如,数组长度为[0,1,21,33,45,5,77,18,99] 9个数 ,数组中元素的 最大值为 99 ,最小值为 0 。我们申请一个大小为 10 的数组,则根据区间放入数组中的每个值。
我们把0-99的范围分为10份,也就是
0-9
10-19
20-29
30-39
40-49
50-59
60-69
70-79
80-89
90-99
每一个范围的数都属于一个桶,因为我们有9个数,分了10个桶,所以至少有一个桶是空桶,也就是总结为:
有n和数和n+1个桶,最左边的桶不空,最右边的桶不空,则中间必然存在一个空桶。n个元素在排序之后,相邻两个元素可能来自同一个桶,也可能来自不同的桶,因此最大差值肯定不来自相同的桶中的两个数
所以我们的思路是
每个桶记录三个信息:是否有数字存入;存入的最小值;存入的最大值。
然后依次计算连续的非空桶之间的差值(即前一个非空桶的最大值和后一个非空桶的最小值之间的差值)。
不去直接找空桶两侧的桶之间的差值的原因如下图所示:
空桶两侧的差值为11,后两个非空桶差值为19,所以最大差值不一定就是来自两个空桶之间,空桶的策略只是为了表示 最大差值一定不来自桶内部。
代码如下:
/* 参数1:数值m 参数2:数组的长度n(桶的长度为n+1) 参数3:数组的最小值 参数4:数组的最大值 返回值:数组m在 n+1 个桶中属于第几号桶(桶从0开始,结尾为n) */ int calculate_inter(int num, int len, int smallest, int largest) { return (num - smallest)*len /(largest - smallest); } //计算最大的距离 int MaxGap(vector<int>input) { int len = (int)input.size(); if (len == 0 || len == 1) return 0; //找到数组中的最大值和最小值 int smallest = input[0], largest = input[0]; for (int i = 0; i < len; i++) { if (input[i]>largest) { largest = input[i]; } else if (input[i] < smallest) { smallest = input[i]; } } //如果最大值等于最小值,则 if (largest == smallest) { return 0; } //申请尺寸为 N+1 的数组(初始化长度为len+1,初值为0) vector<int>mins(len+1),maxs(len+1); vector<bool>hasNum(len+1); //根据最大值最小值分段 int interval =0; for (int i = 0; i < len; i++) { //确定该值属于哪一个桶 interval = calculate_inter(input[i], len, smallest, largest); //得到这个桶内的最大值和最小值 mins[interval] = hasNum[interval] ? min(mins[interval], input[i]) : input[i]; maxs[interval] = hasNum[interval] ? max(maxs[interval], input[i]) : input[i]; hasNum[interval] = true; } //找最大的gap int answer = 0, nowLargest = maxs[0]; for (int i = 1; i <= len; i++) { if (hasNum[i]) { //每次计算一个非空桶m和离m左边最近的非空桶的差值,找到最大的保存下来 answer = answer < mins[i] - nowLargest ? mins[i] - nowLargest : answer; nowLargest = maxs[i]; } } return answer; }
参考:
1 牛客网
2 https://blog.csdn.net/developer1024/article/details/79770240