代码随想录--数组--二分查找题型

使用二分法的前提条件是数组为有序数组并且数组中无重复元素,看到题目描述满足如上条件的时候就要想一想是不是可以使用二分法了。

二分法逻辑比较简单但是很多细节写不好就导致会有很多问题,主要就是对区间的定义没有想清楚。写二分法,区间的定义一般分为两种,左闭右闭即[left,right],或者左闭右开[left,right)。

代码随想录之二分法--https://www.bilibili.com/video/BV1fA4y1o715

对于二分法的套路一般是这样:

left=0;

right=numsize-1/numsize;

while(left<=/

//这里到底是<=还是=呢?

mid=(left+right)/2;

(//我的问题,这里有个小细节,注意mid的值是放在循环里赋值得到的,这样每轮的mid值才是变的,我容易放到while外面,这是错的,要小心一点)

if(nums[mid]>target){   

//如果target小于mid则右区间就应该往左边移,那right是=mid还是mid-1?

right=mid-1/mid; }

else if(nums[mid]

//如果target大于mid则左区间就应该往右边移,那left是=mid+1还是mid?

left=mid+1/mid;}

else return mid;

}

return -1;

对于上面的疑问,[left,right]、[left,right)的不同就会导致上面的疑问结果不同,区间的定义就决定了二分法的代码该如何写,这个很重要。

第一种写法,我们定义target是在一个左闭右闭的区间里,也就是[left,right]。

·上面第二个疑问那里应该是right=mid-1,因为你前面就判断出了mid小于target,你有边界就是为了判断与target的大小从而缩小边界找到target的,那你之前都判断了mid小于target,那下一轮判断中就没有必要再判断这个mid是否小于target,因为铁定小于target,所以应该right=mid-1,让mid的前面的值去与target判断。

·同理else if (num[mid]

第二种写法,我们定义target是在应该左闭右开的区间里,也就是[left,right)。

·左闭右开,应该是while(left

·if(num[mid]>target)应该right是=mid,因为你前面就判断了mid大于target了,现在就是不包含middle在这个右区间了,所以不需要再重新拿mid前面的和target比较了就不需要mid-1。

·else if那个现在是要定左边界了,而左边界是闭合的,同上面左闭右闭的理,这里就应该是left=mid+1

还有一个小细节,左闭右闭时,right是=numsize-1,左闭右开时right是=numsize

练习题:

1.leetcode 704 二分查找

http://代码随想录 https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html#_704-¦ᄎフ¥ネニ₩゚ᆬ₩ノᄒ 

2.leetcode 35.搜索插入位置       

http://代码随想录 https://programmercarl.com/0035.%E6%90%9C%E7%B4%A2%E6%8F%92%E5%85%A5%E4%BD%8D%E7%BD%AE.html#₩タン│ᄋᆵ

3.leetcode 34.在排列数组中查找元素的第一个和最后一个位置

http://代码随想录 https://programmercarl.com/0034.%E5%9C%A8%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E6%9F%A5%E6%89%BE%E5%85%83%E7%B4%A0%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E5%92%8C%E6%9C%80%E5%90%8E%E4%B8%80%E4%B8%AA%E4%BD%8D%E7%BD%AE.html#₩タン│ᄋᆵ

这道题是这样子的:

代码随想录--数组--二分查找题型_第1张图片寻找右边界:

代码随想录--数组--二分查找题型_第2张图片

代码随想录--数组--二分查找题型_第3张图片

例如 :[3,3]找2 ,[3,3]找4, [3,6,7]找6, [5,7,7,8,8,10]找8

①要注意,计算出来的右边界是不包含target的右边界的,左边界同理。

为什么是这样呢?比如[8,8,9]找8,只有你找到第二个8的后一个9的时候你才知道第二个8的右边已经没有8了吧,第二个8的右边是9了,所以你才知道要停下来,所以右边界应该是找到最右边的那个8 的后一个值即9,不然如果你找到第二个8就停下来你怎么知道后面还是不是8了怎么知道还要不要再再往右边找。左边界也同理。

②好好理解这句话:

“当nums[middle]==target的时候,更新left,这样才能得到target的右边界。”

比如[8,9]找8,mid=8即还是等于target的,所以left往右走,left=8更新为left=9,这时候即到了要找的最右边的8的后一个值9,之后left就不会再走了,因为再右没有<=8的了。

再比如[8,9,10]找8,mid=9是大于target的,所以8是在左区间,此时应该缩区间,即最终[8],继续,mid=8是等于target的,所以left往右走,left=8更新为left=9,这时候才到了要找的最右边的8的后一个值9。

所以说,当mid=target的时候,left就能继续往右边走,就能继续走到最右边target的再右一个值即得到target的右边界。

寻找右边界中,只要mid<=target,left就会一直往右走。mid

当出现[8,9,10]找8,不用担心说left到9了而right还在10难道要一直循环,因为再进入循环,这时候因为mid=9大于target所以说明target在左区间,所以right会往左移right=mid-1,最终right会

 因为最终left最终一定会等于最右边target的后一个值,并且在此处停下,所以让右区间rightborder每次都跟着left等于left。右边界同理,这就是为什么让右边界等于left,让左边界等于right。

③那为什么寻找右边界的时候是让left往右移而不是让right往左边移动去寻找右边界?

比如[5,6,7]找6,如果right来寻找右边界,那right=7时就应该停下来,因为7就是要找的6的后一个值,但是这时候right并不知道前一个就是6阿,它都还没往前走所以不知道前面一个就是6,所以它会继续往左走,那就错过正确的右边界值了呀;而如果让left找右边界,它会经过6到达7,就知道7就是6的后一个了,所以就知道要停下来阿,就能找到右边界啦。寻找左边界也是同理啦。

④像[3,3]2,2的最右边的右边界连人家数组的最左边都靠不上的话,那它就不可能在数组范围里啦,那就应该返回[-1,-1]啦,[3,3]4这种同理。

4.leectode 69.x的平方根

二分法还可以解决数的开根号问题。

如这道题,比如找x假设是8的开根号ans,据天怒可知去掉小数部分,所以是2.那咋找呢?

让left=0,right=8,然后二分查找,当mid*mid<=x,left就往右移动即=mid+1,让ans=mid,一旦有mid*mid>x,left和ans就不会移动了,此时是right往右移动=mid-1,最终符合left

5.leectode 367.有效的完全平方数

也是差不多的,只不过这个是mid*mid=x就返回真即可

你可能感兴趣的:(数据结构与算法,算法,数据结构,排序算法)