一.算法简介
这是一个快速搜索的方式,也是一个比较常见和基础的算法。目的是寻找一列集合中的某一个元素的位置,一个检索
假设这里有一列集合,集合里面有个元素,如何找到目标元素的位置?
当然,这个集合还是有讲究的,一定是严格单增或单减的集合列!这也是二分法的使用条件。
(本文用单增来说明,单减类似处理
普通人的思想就是一个一个比对:从集合中的一个元素开始,若第个元素就是,则输出
后来这个想法又有了新的改进,也就是二分搜索。二分搜索就是对半砍,舍掉一半,大大增加了效率。
思路如下:
!!!核心就是和中间元素比大小!!!
(1)先找到中间的元素
(2)做一下简单的高中生都会的分类讨论
①若中间元素等于目标元素,恭喜你,直接中奖!直接返回中间元素的下标
②若中间元素大于目标元素,说明你的饼画大了,目标元素在第一个集合中,对第一个集合中间元素继续进行比大小操作
③若中间元素小于目标元素,说明你的饼画小了,目标元素还未涵盖进去,因此目标元素在第二个集合中,对第二个元素进行继续比大小操作
tips:这里涉及到一个循环,那么就涉及到如何跳出循环。因为整个集合的数字不会变,做运算的是下标,所以就设定左边下标小于等于右边下标时(一定要有等于,后面会解释),循环进行,不符合条件就跳出循环。
(3)若题目给你开了一个小玩笑,集合里面根本没有你要的元素,则返回-1
这就是二分法的步骤了,请注意一个细节,返回值的问题,什么时候返回,一个是特殊情况,没有目标元素,另一个就是目标元素等于中间元素,这也是循环结束的条件。
二.基本格式(八股文
int down =0;
int up =nums.size()-1;
while(up>=down){
int mid=(up-down)/2+down;
int a=nums[mid];
if(a==target)
return mid;
else if(target>a)
down=mid+1;
else
up=mid-1;
}
return -1;
解释:
第一步先定义好集合的左端的下标down,定义为0;再定义集合的右端下标up,为集合的长度-1(因为我们第一个元素下标是从0开始的,所以每个元素下标相对应的要-1)
第二步就是找中间元素a,用while循环做,先找到中间元素的下标mid。
中间元素下标的寻找方法很简单,一种是(首+尾)/2,另一种是首+(尾-首)/2。我们通常会使用后者来计算中间元素下标,因为这样可以有效的防止数据的溢出。让下标对应好集合中的元素。
然后就是比较大小了,首先注意是目标元素和中间元素比大小,不是中间元素的下标和目标元素比大小。是谁这么铸币,我不说。
若中间元素等于目标元素,返回下标mid,若中间元素小于目标元素,取第二个集合,也就是让集合的左端变成中间元素的下一个元素(用下标来解释就是让新集合down=mid+1),然后用else表示剩下的情况,即中间元素大于目标元素,让集合的右端变成中间元素的前一个元素(用下标来解释就是让新集合up=mid-1)
三.一些细节(后续如果有新问题会继续补充的
①关于while的判断条件是否带等号?
我的建议是一定要带的,因为在代码运行的阶段,区间长度是不断的减小的,跳出循环的上一步一定是左边下标down=右边下表up,在数学中表示的是只有一个数的集合(区间),也就是一个点,这样,下标值即可以取左down,也可以取右up,(左右都是相等的数)也是防止自己混淆。
②关于重新定义集合的上下界线,为什么要mid+(-)1,而不等于mid?
因为如果你直接让up=mid或者down=mid,区间的可能会一直不变,对mid进行加减处理也就是为了让区间长度严格单减,防止陷入死循环。
例如:对于集合[1,2,3],你要找3,第一步,down=0,up=2;mid=0+(2-0)/2=1
而mid所对的元素为2,2<3,所以取第二个集合
第二个集合的down=mid=1;up依然是2;新mid=1+(2-1)=1(这里除是计算机的除,取的是整数部分)
而mid所对的元素为2,2<3,所以取第二个集合
……
看见没?开始循环了,所以交上oj是的结果是超时
所以防止超时的一个手段就是新的集合不包含中间元素,取第一个集合就是mid-1,第二个集合就是mid+1.