关于二分查找的一些总结

最近二十天基本上在复习算法和数据结构,对其中一些力扣做了一些总结,今天将二分查找法的应用总结一下:
基本上分为三种类型:
1、 在排序数组中,找某个值,属于常规的二分查找法的应用

// 这里写一下 二分查找法在一有序数组中找某个值,并返回其下标;
int find_the_index(int arr[],int start,int end,int target) //在arr数组中,从下标start到end中找值为target的数
{
    if(start>end)return -1; //代表返回错误或者没有找到
    while(start<=end)
    {
      int mid=start+(end-start)/2;
      if(arr[mid]=target)return mid;
      else if(arr[mid]>target)end=mid-1;
      else start=mid+1;
    }
    return -1;   // 走到这一步说明 start>end 说明没找到
}

// 突然想到也可以用递归,因为查找的过程是一样的,不过递归效率不如循环好,数据很多的情况下,递归的深度非常深可能会内存栈溢出了,当作练习把
int digui_find_the_index(int arr[],int start,int end,int target)
{
    if(start>end)return -1;
    int mid=start+(end-start)/2;
    if(arr[mid]==target)return mid;
    else if(arr[mid]>target) return digui_find_the_index(arr,start,mid-1,target);
    else return digui_find_the_index(arr,mid+1,end,target);
}

2.1、 在排序数组中找到 >=k 最左边的值,比如:1,1,2,3,3,3,4,4,5中 >=3最左边的值是第一个三,也是用二分查找法,因为序列有序,但是唯一不同的是这个循环不会因为找到某个数提前结束!会一直找完,找到没有数可找,然后返回,看代码:

// 在排序数组中找到 >=k 最左边的值,并返回其下标
int find_the_left(int arr[],int start,int end,int target)
{
    if(start>end) return -1;
    while(start<end)  // 这里注意不要等于end,如果等于end也循环,那么end=mid会一直循环下去
    {
       int mid=start+(end-start)/2;
       if(arr[mid]<target)start=mid+1;
       else end=mid;
    }       // 这里注意,循环不会提前停止,直到start>end才停止,而由end=mid这一句可知end不会越界,返回end
    if(arr[end]<target)return -1;  // 压根没有大于等于 k的数,返回-1;
    else return end;  
}

这里注意的是,之所以能找到最后一个,找完结束循环,是因为C++中L+R之和除以2靠近0,就是靠近最左边的数,所以可以找到,下面做出小于等于k最右边的数就不一样了!

2.2、在有序数组中找到 <=k 中最右边的数

// 找到有序数组中<=target最右边的数,如:1,2,2,3,3,4,5中小于等于2的数最右边的数是最右边的2
int find_the_right(int arr[],int start,int end,int target)
{
    int temp_value=start;  //临时变量,为了访问前一个数据
    if(start>end)return -1;
    while(start<end)  //同上面一样,循环结束的条件就是start=end时
    {
       int mid=start+(end-start)/2;
       if(arr[mid]>target) end=mid;
       else start=mid+1;
    }
    if(arr[end]<=target)return end;
    else if(end-1>=temp_value && arr[end-1]<=target)return end-1;
    else return -1;   //找完了,此时end所对应的数都不小于等于target,说明压根没有小于等于k的
}

这里注意一点!因为C++中L+R相加除以2在L和R相差为1时会得到L,例如:(3+4)/2=3,为了防止死循环,让满足情况时L+1,最后有可能L+1处不是满足的条件而L处是的,所以最后再检查是L+1还是L,所以用temp_value保存第一个start值也可以理解了,防止end走到最前面,整体没有比k小的数,判断时越界。

3、局部最小问题
拿一个见过的题来举例,具体在哪看的题目忘记了,一个无序的数组中,找任意一个局部最小值,什么是局部最小呢,如果a是第一个数,那么a小于下一个数a就是局部最小,如果a是最后一个数,如果a小于前一个数那么a就是局部最小,如果这两个都不满足,中间一定会有一个局部最小!因为,左边开始处是下降的,右边结束处是上升的,那么中间必然有个数是局部最小,一个拐点,好,找出任意一个数就可以,(这里的数字都不重复);其实这也是一个二分可以解决的问题,在logn的时间内可以解决,看代码:

// 思路,首先判断两端,如果两端有局部最小,那最好了,直接返回,如果没有,就是会出现中间的
//拐点的情况,找到中间的数,看是否为局部最小,如果是返回,如果不是,要么这个数大于左,要么大于右
//要么两者都有,随便选择大于的一边,比如左,那么在左半边就必然还是有拐点,同样如果大于右边,那么右边同样也有拐点
int find_the_point(int arr[],int length)
{
   if(length<=1)return -1; // 数量小于两个不可能出现局部最小
   if(arr[0]<arr[1])return 0;
   if(arr[length-1]<arr[length-2])return length-1;
   // 上面情况都不满足时,中间必有拐点
   int l=0,r=length-1;
   while(l<r)
   {
   int mid=l+(r-l)/2;
   if((mid+1>=length||arr[mid]<arr[mid+1])&&(mid-1<0||arr[mid]<arr[mid-1]))return mid;
   else if(mid+1<length && arr[mid]>arr[mid+1])l=mid+1;
   else r=mid-1;
   }
   if((l-1<0||arr[l]<arr[l-1])&&(l+1>=length||arr[l]<arr[l+1]))
   return l;
   return -1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值