二分法不难,看完这篇文章,你必懂。
假如我们遇到了一道算法题,要求我们从数组A=[a,b,c,d,e] 里找到d,那通常我们会逐个遍历,遍历a,b,c,d一共需要对比4个数字才能找到d。
那如果使用二分法呢?如何使用二分法?
1.首先我们设置两个指针指向该数组的开头和结尾,
L指向开头,R指向结尾。
2. 我们找到两个指针的中间点mid,(此时L为1,R为5,通常情况下mid=(L+R)/2)=3
3. 对比mid和我们所需要找的数之间的大小,我们要找的是D显然C<D,此时将L挪到mid的位置,然后算出新的mid。
(如果本题要找的是B,同理,mid所指向的C大于B,则将R放到mid的位置然后算出新的mid。)
4. 重复上面的步骤继续对比mid所指向的数是不是等于我们要找的数。mid=D 是我们要找的数,结束。
5. 如果L都移动到R的右边了还没找到我们要的数,则可以判断,这堆数里没有我们要找的数。
上面就是二分法的基本思想,可以看到,如果使用普通方法找D我们需要对比A,B,C,D 四次,而使用二分法我们只对比了C和D 2次就找到了。
杂谈:
关于二分法还有相当多的变种,现在讲必然会乱到炸。比如移动的时候不是直接将R或者L放到mid 而是放到mid+1 或者mid-1。。而在L和R的设置上也有开闭区间等多种,在mid计算上也有向下向上取整的问题。
这些都不要管!!!!!!!!!!!!!!!!!!
只需要懂得上面的思想,然后拿题去一点一点做去试,一开始必然都是错误,返回去继续想一遍流程,再改,这些都会融会贯通的。
怎么去试呢?
下面我用一个例题,让你从此以后彻底掌握二分法。(其他算法那也可以用此方法融会贯通)。
例题:
这里使用的例题:leetcode 374题。
题目给了一堆数共n个。 其中只有一个是正确的数,你要猜一下这个正确的数是几。
只不过你猜的这个数要通过一个函数去判断你猜的对不对。
如果函数返回了 1 说明正确的数比你猜的数要大。
如果函数返回了 -1 说明正确的数比你猜的数要小。
如果返回了0 说明你猜对了。
那么按照上面的算法思想写一下代码。
设置L和R指向头和尾 那就是
L = 1
R =
如果L一直在R左边,那就一直算mid。
while l < r:
mid = (l+r) // 2
每次算出来的新mid,调用函数看他是不是正确的数。
if guess(mid) == 0:
print(‘这是正确的数’)
elif guess(mid) == -1:
r = mid
elif guess(mid) == 1:
l =
我们吧上面的代码拼接起来(别忘了print改成return)
拿去leetcode跑一遍测试用例发现,当n=2,所要找的数=2时。
超时了。。。。。。。。
超时的原因肯定是while没有出来。于是我们将 n=2 ,正确的数=2带入到上面的错误程序中。
首先
L = 1
R = 2
算出mid =(L+R)/ 2 = 1
此时发现L和mid都指向1,显然我们要找的2是比1要大的,所以调用的guess函数返回了 1 执行代码:
elif guess(mid) == 1:
l =
本来mid和L就已经指向同一位置了。执行了这段代码等于什么也没干,就这样一直死循环在while里导致程序运行超时。
这时候怎么办呢,我们要找的明显是2。
此时使用二分法的变种(实际就是根据题的需要改变二分法的写法).
我们可以将 L = mid 改为 L = mid +1 这样L就会指向2。
改动代码:
elif guess(mid) == 1:
l = mid +1
还是这个图, 当函数guess返回1的时候 L = mid L和mid都是指向1.程序死循环,而我们改成了 L = mid + 1 ,则此时L会指向 R所指的位置。
此时进入下一次while循环发现L<R 已经不满足无法在进入循环,程序失败。所以将循环退出条件改为 L <= R。进入循环,计算新的mid=2.判断函数返回0,程序正确结束。拿去leetcode进行测试。
测试通过。掌握基本思想,通过例题逐步调试写出正确二分法的变种,更利于学习算法思想。
第一次这样手写讲解算法,如果有不懂的地方或者讲解有错误,留言给我,互相进步。