算法学习-数组的最大间隔

题目

给定整数数组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)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值