算法学习-桶排序的具体例题<高频题目>相邻两数最大插值
问题描述:
给定一个数组,求排序之后的最大差值,且时间复杂度为O(N),额外空间复杂度为O(1)的算法,且要求不能用非比较的排序算法。
问题分析 && 算法实现
- 将待排序元素划分到不同的桶。先扫描一遍序列求出最大值 maxV 和最小值 minV ,设桶的个数为 k ,则把区间 [minV, maxV] 均匀划分成 k +1个区间,每个区间就是一个桶。将序列中的元素分配到各自的桶。
2.根据鸽笼原理(鸽笼原理简单表述:若有n个笼子和n+1只鸽子,所有的鸽子都被关在鸽笼里,那么至少有一个笼子有至少2只鸽子。推广:如果要把n个物件分配到m个容器中,必有至少一个容器容纳至少⌈n / m⌉个物件。(⌈x⌉大于等于x的最小的整数))可以得知至少有一个空桶(空桶的意义在于否定最大插值在同一个桶中出现),
3.去遍历一遍数组,去记录每个桶中的最大值和最小值及每个桶是否是空桶.
4.去找相邻两个桶的最大差值(及后一个桶的最小值和相邻的前一个非空桶的最大值)
5.最后结果即在这些记录的最大值中.即找出这些跨桶最大值中的最大值.
代码实现:
#include<iostream>
#include<cstdlib>
using namespace std;
//求出每一个数所在的桶的编号
int bocketNum(int a[], int i, int min, int max, int len){
return (a[i]-min) * len / (max-min);
}
int Min(int a, int b){
return a <= b ? a : b;
}
int Max(int a, int b){
return a >= b ? a : b;
}
int maxGap(int A[], int n)
{
int min = A[0];
int max = A[0];
//找出最大值和最小值
for(int i = 1; i < n; ++i){
min = (min <= A[i] ? min : A[i]);
max = (max >= A[i] ? max : A[i]);
}
int* minArr = new int[n+1]();//记录每个桶中的最小数
int* maxArr = new int[n+1]();//记录每个桶中的最大数
bool hasNum[n+1] = {0};//记录桶中是否有数
for(int i = 0; i < n; ++i){
//求出每一个数所在的桶的编号
int bocketID = bocketNum(A, i, min, max, n);
minArr[bocketID] = hasNum[bocketID] ? Min(minArr[bocketID], A[i]) : A[i];
maxArr[bocketID] = hasNum[bocketID] ? Max(maxArr[bocketID], A[i]) : A[i];
hasNum[bocketID] = true;
}
int MaxGap = 0;//记录最大差值
int LastMax = 0;//记录当前空桶的上一个桶的最大值
int i = 0;
while(i < n + 1){ //可能会有多个空桶
//遍历桶,找到一个空桶
while(i < n + 1 && hasNum[i])
i++;
if(i == n + 1)
break;
LastMax = maxArr[i-1];
//继续遍历桶,找到下一个非空桶
while(i < n + 1 && !hasNum[i])
i++;
if(i == n + 1)
break;
MaxGap = Max(MaxGap, minArr[i]-LastMax);
}
delete []minArr;
delete []maxArr;
return MaxGap;
}
int main(void)
{
int NUMBER;
cin>>NUMBER;
int *p;
p=new int[NUMBER];
for(int k=0;k<NUMBER;k++)
{
p[k]=rand()%NUMBER;
}
for(int i=0;i<NUMBER;i++)
{
cout<<p[i]<<" ";
}
cout<<endl;
cout<<maxGap(p,NUMBER)<<endl<<endl;
return 0;
}