建议直接看文末的二分查找
题目的意思很简单,一个数组中前面都是0,后面都是1,你可以通过 isBadVersion(int version) 函数来判断version位置处的元素是1还是0。并强调了要尽可能地少调用该函数。那么很显然就是要二分查找了。但是我开始写的二分查找竟然在第11组测试用例就超时了(原因见文末),后来一直也没有找出来错来,自暴自弃地写了一个线性查找竟然撑到了第16组测试用例才TLE。于是我改良了线性查找,先缩小查找的范围,然后在一个小范围中线性查找,这个诡异的想法竟然通过了,代码如下
int firstBadVersion(int n) {
for(;isBadVersion(n/2);) n/=2;
for(;isBadVersion(n);n--);
return n+1;
}
耗时相当多。我企图通过每次变化的幅度来优化时间效率。下面的代码执行用时为0ms
仔细思考之后发现,由于C语言整除时向下取整的特点,只有在n很大的时候, 他才会大概按照每次1/10000的比例缩减,而n不够大的时候,基本上是按照每次接近10000速度减少,因为在第一次除法进行完之后,n的后四位数肯定会变得比较大,比如152563/10000=15
15*9999=149985
这也好理解,因为一个数乘以一万肯定会得到一个后四位都为零的数,而在这里乘以的是9999,后四位肯定略小于10000
就算蒙混过关吧,不推荐
int firstBadVersion(int n) {
for(;isBadVersion(n/10000*9999);) n=n/10000*9999;
for(;isBadVersion(n);n--);
return n+1;
}
但是这种奇技淫巧毕竟不具有普遍性,下面是一个通过且用时0ms的二分查找
int firstBadVersion(int n) {
if(isBadVersion(1)) return 1;
int l=1,r=n;
int mid;
while(l<r)
{
mid=l/2+r/2;
if(isBadVersion(mid)) r=mid;
else l=mid+1;
}
return l;
}
注意其中的mid=l/2+r/2
而不是(l+r)/2,因为后者在l+r较大时会溢出,然后就开始了乱七八糟的循环......
第一篇博客,就这样吧~