LeetCode 278 第一个错误的版本

建议直接看文末的二分查找

题目的意思很简单,一个数组中前面都是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较大时会溢出,然后就开始了乱七八糟的循环......

第一篇博客,就这样吧~


猜你喜欢

转载自blog.csdn.net/Accsc/article/details/80315299