leetcode-二分查找算法总结

最朴素二分查找算法:

最朴素二分查找算法:

最基础的一道题:基于有序数组的二分查找--. - 力扣(LeetCode)

基本流程:

1:实际上二分查找不一定非得要有序,能否使用二分查找的关键是是否具有二段性.

2:基于有序数组的二分查找算法也没有规定必须要均分.对于有序数组不论你取哪个点都具有二段性.只不过在中间取分割点的方式效率最高,时间复杂度为Olog(n);

时间复杂度:

从有序数组的二分查找我们可以抽象出来朴素二分查找的模板:

省略的内容是确定二段性的过程,需要自己根据题目找出确定二段性的方式

不朴素二分查找

不朴素二分查找其实就是二分查找的循环里左右指针的行为为:1 :left = mid + 1,right = mid或者2: left = mid right = mid  - 1这两种时的二分查找。这里给出模板解决上述俩种情况的边界处理问题。

. - 力扣(LeetCode)"在排序数组中查找元素的第一个和最后一个位置"为例:

直接通过朴素二分查找确定的元素无法确定他在目标区间的位置,也就不知道是不是第一个和最后一个位置的元素.所以朴素二分查找无法解决这道题.

这道题我们可以通过题目分解为俩部分:1:找第一个位置 2:找最后一个位置

找目标元素出现的第一个位置即目标元素左端点:

第一段(数组左端到left)为不包含目标元素的区间,第二段(right到数组右端)为包含目标元素的区间。mid指针不断移动对俩个区间进行扩张直到找出左端点。具体来说:

        *如果mid小于target,left = mid + 1;
        *如果mid大于等于target,right = mid;   

        *循环结束条件:while(left < right)                       

这里的二段性怎么分析出来的? 注意这里找的是左端点.左端点就能得出来一个特性,左端点的左边一定是不包含目标元素.由此我们将数组分为:包含目标元素和不包含目标元素俩段.俩段的临界点即我们要去拿到的左端点

        细节:    

                    1:循环结束的判断:一定要为left < right,如果是left <= right 那么在判定到最终结果时,判定条件为if(nums[mid]>=nums[right]) right=mid,此时就会陷入到死循环当中。
                    2:对于mid的确定(元素个数为偶数时mid的位置为偏左一个数还是偏右一个数):mid一定要得到偏左那个数,即left + (right - left)/2.          原因:如果到了left和right指针相邻的情况时,mid落在了右边那个数,那么right = mid,mid再次计算位置不变这个过程就形成了一个死循环。(剩下俩个数左边那个一定小于或者等于嘛,右边那个数一定大于或者等于嘛,结合left和right的移动规则就知道要到偏左那个位置)

模板:

找目标元素出现的最后一个位置即目标元素右端点:

对于右端点来说,它的二段性进行了改变:
第一段:包含target的区间;(0,left]
第二段:不包含target的区间。(right , nums.length - 1)
所以left和right的行为相比较与左端点的求法就需要进行改变:
*mid <= right left = mid
*mid > target right = mid - 1.

这里left和right的移动规则进行了改变,则mid的确定方法就得进行改变。mid需要确定到偏右的位置,否则就会进入死循环,所以要使用:left + (right - left + 1)/2

其它注意事项和原理与求左端点基本相同.

实例:

模板:

在遇到二分查找的问题时,我们首先根据题意确定左右指针的行为,行为确定后如果是 不朴素二分查找我们就直接通过上述模板确定循环结束条件,mid的表达式等。

例题:

不朴素二分查找:

                非递减数组求目标元素的左右端点 :. - 力扣(LeetCode)

                 旋转数组最小值. - 力扣(LeetCode)将数组抽象成图形就会发现其二段性(俩个升序段,且第二个升序的最大值比第一段小):以第一个元素或者最后一元素为flag,mid值比flag大说明mid在第一段,否则在第二段。所以左右指针的行为就应该是:if(nums[mid] < flag)right = mid; else left = mid + 1然后套用模板解决问题

                x的平方根:. - 力扣(LeetCode)举个例子:8的平方根在2和3之间,但是这里的规则是取2即小数直接去掉。所以做指针的行为就应该是if(nums[left]的平方 <= taget)left = mid else right = mid - 1.然后套用模板就能解决问题。

                搜索插入位置:. - 力扣(LeetCode)按照题意不存在目标值target时返回第一个大于目标值的下标。所以左右指针的行为就是:if(nums[left] < target)left = mid + 1  else right = mid

然后套用模板即可

                山脉数组峰顶索引:. - 力扣(LeetCode)

利用山脉的上升和下降通过mid和mid + 1值比较判断二段性。上升阶段left可以压缩到mid + 1下降阶段righ可以压缩到 mid所以left和right行为:

if(arr[mid] >= arr[mid + 1]) right = mid;else left = mid + 1;然后套用模板。

                点名:. - 力扣(LeetCode)二段性:缺席之前下标等于学号,缺席之后下标不等于学号。下标和对应的学号进行比较得到二段性。当 下标 != 学号 时right压缩到mid,当下标 == 学号时left 压缩到mid + 1.由此得到left和right指针行为:if(mid != records[mid]) right = mid;else left = mid + 1;然后套用模板即可

  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值