文/一觉亮天
鸽巢原理经常用在判定存在性的问题。
鸽巢原理: n+1只鸽子往n个巢子里飞,则可以确定至少有一个巢里有大于等于二只鸽子。
m只鸽子往n个巢里飞,
则可以确定至少有一个巢里有大于等于ceiling(m/n)只鸽子。
下面举一个利用鸽巢原理解题的例子。
数轴上的一组无序实数,求出相邻两点间的最大间隙。要求算法的时间复杂度为n。
解此题最容易想到的就是先把这组实数排序,然后用一个循环遍历一遍排序后的序列,就可以找到相邻两点间的最大间隙。
但是我们知道,最快的排序算法的平均时间复杂度都是n*log(n),
如快速排序和归并排序。 而如插入排序和冒泡排序等的时间复杂度为n的平方。所以这种办法是行不通的。
关于此题,我是一直没能想出时间复杂度为n的算法。
直到在书中找到了答案。算法的思路如下。
- 找出这组数的最大值和最小值。
(时间复杂度为n)
- 在最大值和最小值之间等间隔地构造n-1个桶,把除了最大值和最小值的n-2个数按照他们各自的值安排在相应的桶里。
每支桶里只需要记录属于这个桶的最大数和最小数。
(时间复杂度为n)
- 根据鸽巢原理,有n-2个数,n-1个桶,则至少有一个桶是空的。
所以最大间隙只能存在于桶间和桶与最大值和最小值之间,不会出现在桶内。在最小值、各个桶和最大值之间遍历一遍,就可以求出最大间隙。
(时间复杂度为n)
下面是程序代码。
object Pigeon {
def main(args:Array[String]){
//read maximum number of real numbers.
val num=readInt()
//read num double number
val points=new Array[Double](num)
for (i<-0 until num){
points(i)=readDouble()
}
//get the index of minimum & maximum
val minIdx=min(points)
val maxIdx=max(points)
//initialize (num-1) bucket, maximum in 1 while minimum in 2 stands for empty bucket
val bucket=new Array[(Double,Double)](num-1)
for(i<-0 until num-1){
bucket(i)=(points(maxIdx),points(minIdx))
}
//fill double numbers in bucket
for(i<-0 until num){
//not include maximum and minimum
if(i!=minIdx && i!=maxIdx){
var bucket_index =
((points(i)-points(minIdx))*(num-1)
/(points(maxIdx)-points(minIdx))).toInt
var minBucketVal=bucket(bucket_index)._1;
var maxBucketVal=bucket(bucket_index)._2;
if ( minBucketVal > points(i) ){
minBucketVal = points(i)
}
if (maxBucketVal < points(i) ) {
maxBucketVal = points (i)
}
bucket(bucket_index)=(minBucketVal,maxBucketVal)
}
}
//iterate to get max_interval
var max_interval:Double=0.0d;
var previous_val:Double=points(minIdx)
for(i<-0 until (num-1) ) {
var (minBucketVal,maxBucketVal) = bucket(i)
//it is not empty bucket
if(minBucketVal<=maxBucketVal){
var cur_interval=minBucketVal - previous_val
if(cur_interval>max_interval) max_interval=cur_interval
previous_val=maxBucketVal
}
}
var cur_interval=points(maxIdx)-previous_val
if(cur_interval>max_interval) max_interval=cur_interval
//output the result
println(max_interval.toString)
}
def min(ary:Array[Double]):Int={
var idx:Int= 0;
var i:Int=0;
for(i<-0 until ary.length){
if ( ary(i) < ary(idx) ){
idx=i
}
}
idx
}
def max(ary:Array[Double]):Int={
var idx:Int= 0;
var i:Int=0;
for(i<-0 until ary.length){
if ( ary(i) > ary(idx) ){
idx=i
}
}
idx
}
}