二分查找的思想和解题步骤笔记,模板
二分虽然思想简单,做了两天的题下来发现实际应用到题目中并没有想象中那么轻松,在此记录下笔记和归纳总结一下几道题目的ac过程以备以后复习时使用。
二分的思想很简单,以下给出定义。
给定一个升序排列的数组 nums ,在其中寻找某个值target,设两个指针 l r 分别指向> 数组左右边界,每次取区间的中点 l+r>>1 为 mid ,当 nums[mid]>targer 的时候说明元素在 [l, mid-1] 之间,反之则在 [mid+1,r] ,当两种情况都不满足,则 nums[mid] 必定为要寻找的 target
如果光知道定义是不足以应用到题目之中的,在连续做了几天的二分题看了一堆题解后,我发觉要二分的不是某个值,而是满足某个性质的区间的边界,当数组可以依照某种性质一分为二,即一边 满足性质 ,另一边 不满足性质 ,我们就有可能把满足或不满足性质的边界给二分出来,而往往这个边界就是需要返回的答案。这么说多少有点抽象,下面是给出的示例
如leetcode上这道模板题
如这题,我们将数组一分为二,蓝色是满足性质( <=target 的区间),紫色是不满足性质(>target)的区间
我们需要做的就是寻找这个蓝色区间的右边界,每次二分一个中值 nums[mid] ,
当 nums[mid]<=target 的时候,我们的边界必定在 [mid,r] 之间(注意mid是有可能取到答案的),nums[mid]>target 的时候,边界必定在 [l,mid-1] 这个区间,每次都缩小范围。
当使用边界的思想,面对一些无单调性但要求在**logn**时间复杂度内做出的题目时,也不至于手足无措,至少可以尝试寻找是否有一个性质能将区间一分为多(菜鸡落泪)进而寻找到边界
实现代码如下
1 | int search(int* nums, int numsSize, int target){ |
疑惑的一点是取中值的时候为什么是 l+r+1>>1 而不是 l+r>>1 ,这是因为在c中除法是向下取整的,这
样会导致当查找区间为 [r-1,r] 的时候,如果是l+r/2,那么区间会更新为**_[l,r]_,下次查找还是 [l,r] 导致死循环,当mid**落在右区间时,即我们需要寻找的是蓝色区域的边界(满足性质)需要补上+1
记一下千锤百炼的模板
1 | int bsearch_1(int l, int r) |
具有单调性的题一定可以二分,不具有单调性但可以将区间一分为二的题有可能可以二分
在将做二分题的步骤抽象了之后,尝试做了一道题加深印象
leetcode34 数的范围
题目要求:
按上面的思路将题目翻译一下,我们要寻找的是 第一个和最后一个 >=targert 的数,即求上下界的索引位置
以 [5,7,7,8,8,10] 为例,首先我们需要寻找一个性质,能将数组一分为二,不难发现我们可以分为以下两个区间
即 <target 的数分为蓝色区间, >=target 的数分为紫色区间
可以看到,我们要取的元素是紫色区间的左边界。设 mid 为数组的中值,之后判断是否满足 nums[mid]>=target,
如果是的话,那么要查找的元素肯定在 [l,mid] 之间(_因为nums[mid]如果大于target,后面的元素就不可能是target,如果nums[mid]==target,也有可能是最后一个target,我们需要在前半段继续查找)_,当 l=r 时区间里只有一个元素,也就找到了第一个target
这里的二分这么写1 | ..... |
接下来的查找最后一个 >=target 的元素就并不那么容易了,同样我们依照上面的性质将区间一分为二
蓝色区间满足 <=target 紫色区间满足 >target
我们需要查找的元素,实际上是蓝色区间的 右边界 ,现在我们要把它二分出来
同样取中值 mid,判断 nums[mid]>target 是否成立,如果成立的话,答案一定在 [l,mid-1] 这个区间里,如果不成立(即小于等于target),我们将区间缩小为 [mid,r] (_分两种情况讨论,如果nums[mid]==target的话,不一定是最后一个元素,我们需要继续往右边查找,将区间设置为 [mid,r]_)
1 | while(l<r){ |
1 | int* searchRange(int* nums, int numsSize, int target,int* returnSize){ |
本文标题:二分查找的思想和解题步骤笔记,模板
文章作者:meteor
发布时间:2022-05-30
最后更新:2022-05-30
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!
分享