二分查找

文章目录


思想: 有序吗?找值吗?都满足=》二分 两个条件缺一不可
如果无序但是想不到别的方法了,非要二分?可以 =》 手动排序

两个模板

1 while(left<=right)
在循环体内部查找元素
循环体内部的判断通常有三个
1、 nums[mid] = = target
2、nums[mid] < target
3 、nums[mid] > target
当循环退出时,left一定在right的右边,需要根据情况判断最终的返回值
当正常的顺序数组时很适合,变形之后的比如旋转数组就不适合了
在这里插入图片描述

2 while(left<right)
循环中基本两次判断
不在循环中返回,退出循环再返回。
优点是当退出循环时,一定有left==right ,不用纠结返回什么,可以少考虑很多细节
但是此法有一个问题:
就是当如图:
在这里插入图片描述
此时 int mid=left+(right-left)/2;得到的mid就是left
但是如果check(mid)返回false则会执行left=mid(只要有left=mid就要注意)。从此陷入死循环
解决办法:
int mid=left+(right-left+1)/2
向上取整即可避免此情况。
在这里插入图片描述
思路爆炸的一题
在排序数组中查找元素的第一个和最后一个位置
思路:不要怕,题目怎么要求就怎么做,要找左右边界,那就先找左边界,再找右边界。
一 、找左边界
因为是顺序数组,所以无论怎样,left<=right
因为left<=right 所以while中三次判断,严格执行就完事了
1 、nums[mid] == target 我要找左边界,所以 right = mid - 1;
2、nums[mid] < target left=mid+1
3 、nums[mid] > target right = mid - 1
循环内没有返回 那就在循环外返回 ,返回谁?
left right -1 就这三个没跑,试一下就知道left还是right,此时是left
什么时候返回left?当然是 nums[left] = = target的时候,否则就是-1
但是报错数组下标越界,只可能是nums[left] = = target这里越界啊,left为啥会越界? 很容易想到当target很大时啊,试一下果然是的,那就判断呗,当
left != nums.length/left<nums.length都可以。
二、找右边界
照着左边界写就完事了。

##########################################
山脉数组的峰顶索引

思路:
由于是找最大值的下标,且最大值左右两边都有数,所以采用nums[mid]和nums[mid+1]比较的方式,如果右边比我大,就继续往右边找,否则往左边找。
关键:
在这里插入图片描述
2
由1的思想进一步解决山脉数组中查找目标值
思路:先找到峰值索引,由峰值索引将数组分为左边升序,右边降序。

寻找峰值
多个峰值中找出其中一个。多看下官方题解理解一下。

3 旋转数组 有别于峰值数组
详细题解旋转数组
注意:不能用题1的解法找到峰值点然后+1得到最小值点。
因为峰值点可能就是第一个元素,此时是找不到峰值点的,返回的一定是最后一个元素,+1之后数组下标越界
思路:
1 左值 < 中值, 中值 < 右值 :没有旋转,最小值在最左边,可以收缩右边界

       右
     中
 左

2 左值 > 中值, 中值 < 右值 :有旋转,最小值在左半边,可以收缩右边界

 左       
         右
     中

3 左值 < 中值, 中值 > 右值 :有旋转,最小值在右半边,可以收缩左边界

     中  
 左 
         右

4 左值 > 中值, 中值 > 右值 :单调递减,不可能出现

 左
    中
        右

分析前面三种可能的情况,会发现情况1、2是一类,情况3是另一类。

如果中值 < 右值,则最小值在左半边,可以收缩右边界。
如果中值 > 右值,则最小值在右半边,可以收缩左边界。
通过比较中值与右值,可以确定最小值的位置范围,从而决定边界收缩的方向。

而情况1与情况3都是左值 < 中值,但是最小值位置范围却不同,这说明,如果比较左值与中值,不能确定最小值的位置范围。

所以需要通过比较中值与右值来确定最小值的位置范围,进而确定边界收缩的方向。

4
搜索旋转排序数组
思路:先找出最小值的位置以此分割出左右两个有序数组,然后由target和 nums[0] 的大小关系判断出target在那边,再在相应的这边做二分
5
搜索旋转排序数组2
思路:相较于第4题,数据可以重复,那就当重复时向右移动,一直移到最后一个值即可

//可重复的 就多了这一步而已
            if(nums[left]==nums[mid]){
                left++;
                continue;
            }

#############################################3
6
面试题 08.03. 魔术索引
就用一次遍历算了,二分法太难理解了,想到就更不可能了。

7
搜索插入位置
思路:由于是顺序数组,所以 left<=right ,偏向左边所以最后返回left
8
x 的平方根
思路:二分没问题,right=x/2没问题。相乘就一定要考虑溢出。向下取整(偏向左边),所以返回right
9
0~n-1中缺失的数字
思路: 当A[n]=n时,下标一定在右边,否则目标可能就是n,也可能在左边。

class Solution {
    //最后一定是 left指向右子数组的首位元素,right指向左子数组的末位元素
   public int missingNumber(int[] nums) {
    //    if(nums.length==1) return nums[0]==0? 1:0;
        int left=0,right=nums.length-1,mid;
        while (left<=right){
            mid=(left+right)/2;
            if(nums[mid]==mid) left=mid+1;
            else  right=mid-1;
        }
        return  left;
    }

}

但是下面这样不行:

 while (left<right){
            mid=(left+right)/2;
            if(nums[mid]==mid) left=mid+1;
            else  right=mid;//按理说应该是=mid而不是mid-1 
            //因为mid可能就是第一个不匹配的位置
        }

###################################
二维数组找值 官方题解 没啥好说的 顺序数组没重复找值==二分
row= length//n
col=length%n

二维数组找值2
自己:每一行二分 O(nlogm)
官方法四很好

有序矩阵中第K小的元素,和二维数组找值类似
官方法三,题解还没完全搞懂。
##############################################

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值