为了最大化时间和效率(偷懒 ),不打算给新生上课讲二分,而是选择以的形式给出。
对于新生来说二分这个词语肯定比较陌生(少部分做过郑轻的题的人应该看到过这个词),下面我将整个分成两部分给大家入门指导。
这里先提一下复杂度:
在竞赛中,一般计算机一秒能运行次汁算,如果题目給出的时间限制为,那么你选择的算法执行的汁算次数最多应该在量级オ有可能解决这个题目。
一般
的算法能解决的数据范围在。
的算法能解决的数据范围在。
的算法能解决的数据范围在
的算法能解决的数据范围在。
的算法能解决的数据范围在。
的算法能解决的数据范围在。
的算法能解决的数据范围在。
以上范围仅供参考,实际中还要考虑每种算法的常数。
二分的原理:
二分二分,顾名思义,就是将查找的区间分成两半,找中间的部分,然后判断查找左半边还是右半边。
很显然,二分有一个非常重要的条件:查找元素必须有序
不然就难以判断它到底往左找还是往右找
同时,要注意二分查找和二分答案在原理上相同,但二分查找用于查找元素,而二分答案更像是枚举算法的优化,在做题时不要弄混。
那么,二分到底优秀在哪呢?
二分查找
上面介绍到,评价一个算法,我们可以从时间复杂度和空间复杂度来判断:
二分查找的空间复杂度:,和一般算法相同
二分查找的时间复杂度:,从这里就可以看出二分查找优秀了很多。
比如我们有105个元素,一般的方法在最坏情况下应查找105次,如果是二分的话,只需要次即可。
以在一个升序数组中查找一个数为例:
它每次考察数组当前部分的中间元素,如果中间元素刚好是要找的,就结束搜索过程;
如果中间元素小于所查找的值,那么左侧的只会更小,不会有所查找的元素,只需要到右侧去找就好了;
如果中间元素大于所查找的值,同理,右侧的只会更大而不会有所查找的元素,所以只需要到左侧去找。
在二分搜索过程中,每次都把查询的区间减半,因此对于一个长度为 的数组查找等于的数,至多会进行
第一步:排序(使得数组具有单调性)
做二分查找题,在题目没有明确表示的情况下,我们得先进行预处理
即给数组排序
第二步:确定边界
开始二分查找前我们要确定查找的边界,即这个数可能在第几到第几的范围内出现。
很显然,这题我们并不能直接圈定一个范围,那么我们就无脑直接将范围设成整个数组
我们以表示左边界表示右边界
显然,左右边界在查找过程中会发生改变,所以接下来有一个问题,见下文。
预处理结束边界也确定了,我们就可以开始查找了。
第三步:开始查找
我们之前提到了,分左右之后找中间的部分,也就是最中间的那个。
查找肯定不能一次就结束啊,所以我们要用循环。
设置变量为当前查找的位置。
一般用循环:
这里的 代表二进制右移一位,即 ,占用时间较少。
注:位运算的优先级低于加减乘除模(所以我没有用括号 )
接下来我们要进一步判断是向左找还是向右找。
设置类型的变量代表有没有找到,初始为。
很好理解吧, 当前元素比查找元素大则我们在查找元素的右边,反之亦然。
那么有新生会注意到,为什么和 要 呢?
假定此时,,得 ,如果此时不是查找元素的话,那么如果当前元素比查找元素小,那么我们调整 的值。
如果不的话会发生什么呢? 再次被赋值为,则进入无限循环,所以我们需要,来改变我们要查找的值的可能存在的区间。
则核心代码如下:
接下来我们举个栗子。
假定数组为:(为起点
我们来找这个元素
程序开始:
程序结束。
真的很短很快有木有?!
那么二分查找可以应用在哪里呢?以后会用到 ( 及 的 版就可以用到二分查找。)
接下来就是更喜闻乐见丧心病狂的二分答案
二分答案:优雅的暴力,将枚举复杂度降低
例题:Merry Christmas(本来想在这次新生赛出的,但是怕被打 )
題目链接:https://nuoyanli.com/problem/1916 二分答案入门题。
看到这道题,大多数人的想法应该是暴力枚举切段长度吧。
别说我特意试了一发暴力:(自己造的数据,自己卡自己 )
参考
你可以转头一看,数据范围——
暴力,不存在的!
于是我们便要用到在答案可能在的范围中二分查找我们的答案了。
对这道题来说,答案的范围就是从1到 最长的原木长度。
于是,我们设
好了,范围解决了,接下来又面临一个问题了,怎样判断答案可不可行呢?
没办法了,只能暴力了。
每根原木扫一遍,算出能切多少根,再加起来和 比较一下即可
判断的问题解决了,怎么判断下一步搜索的范围呢?
很显然,如果切得太少,那么长度太长,
如果切得太多,虽然达到了要求,此时我们可以记录答案,但长度可能太短,可能有更优解,那么
二分详细代码如下:
写的有点多,希望对大家有所帮助,有问题的欢迎24小时问我。()