题目
分析给定整数数组A[0...N-1],求这N个数排序后最大间隔。如1,7,14,9,4,13的最大减个为4。
排序后:1,4,7,9,13,14,最大间隔是13-9=4
显然,对原来数组排序,然后求后项减前项的最大值,即为解,但还有更好的方法。
假定N个数的最大最小之为max,min,则这N个数形成N-1个间隔,最大间隔的最小值是(max-min)/(N-1)
如果N个数完全均匀分布,则间距全部是(max-min)/(N-1)且最小
如果N个数不是均匀分布,间距不均衡,则最大间距必然大于(max-min)/(N-1)
解决思路
将N个数用间距(max-min)/(N-1)分成N-1个区间,则落在同一区间内的数不可能有最大间距。统计后一区间的最小值与前一去见的最大值的差即可。
若没有任何数落在某区间,则该区间无效,不参与统计。显然,这是借鉴桶排序/Hash映射的思想。
同时,N-1个桶是理论值,会造成若干个桶的数目比其他桶大1,从而造成统计误差。
如:7个数,假设最值为10、80,如果使用6个桶,则桶的大小为70/6=11.66,每个桶分别为[10,21]、[22,33]、[34、44]、[45、56]、[57、68]、[69、80],存在大小为12的桶,比理论下界11.66大。
代码如下
typedef struct tagSBucket
{
bool bValid;
int nMin;
int nMax;
tagSBucket() : bValid(false){}
void Add(int n)
{
if (!bValid)
{
nMin = nMax = n;
bValid = true;
}
else
{
if (nMax < n)
nMax = n;
else if (nMin > n)
nMin = n;
}
}
}SBucket;
int CalcMaxGap(const int* A, int size)
{
// 求最值
SBucket* pBucket = new SBucket[size];
int nMax = A[0];
int nMin = A[0];
int i;
for (i = 1; i < size; i++)
{
if (nMax < A[i])
nMax = A[i];
else if (nMin > A[i])
nMin = A[i];
}
// 一次将数据放入桶中
int delta = nMax - nMin;
int nBucket; // 某数应该在哪个桶中
for (i = 0; i < size; i++)
{
nBucket = (A[i] - nMin) * size / delta;
if (nBucket >= size)
nBucket = size - 1;
pBucket[nBucket].Add(A[i]);
}
// 计算有效桶的间隔
i = 0;// 首个桶一定是有效的
int nGap = delta / size; // 最小间隔
int gap;
for (int j = 1; j < size; j++) // i是前一个桶,j是后一个桶
{
if (pBucket[j].bValid)
{
gap = pBucket[j].nMin - pBucket->nMax;
if (nGap < gap)
{
nGap = gap;
}
i = j;
}
}
return nGap;
}
时间复杂度是O(N),空间也是O(N)