二分查找模板

一、查找精确值

从一个有序数组中找到一个符合要求的精确值(如猜数游戏)。如查找值为Key的元素下标,不存在返回-1。

//这里是left<=right。
//考虑这种情况:如果最后剩下A[i]和A[i+1](这也是最容易导致导致死循环的情况)首先mid = i,
//如果A[mid] < key,那么left = mid+1 = i +1,如果是小于号,则A[i + 1]不会被检查,导致错误
int left = 1,right = n;
while(left <= right)
{
   //这里left和right代表的是数组下标,所有没有必要改写成mid = left + (right - left)/2;
  //因为当代表数组下标的时候,在数值越界之前,内存可能就已经越界了
  //如果left和right代表的是一个整数,就有必要使用后面一种写法防止整数越界
        int mid = (left + right) / 2;
    if(A[mid] == key)
      return mid;
    else if(A[mid] > key)//这里因为mid不可能是答案了,所以搜索范围都需要将mid排除
      right = mid - 1;
    else
      left = mid + 1;
}
return -1;

 二、查找符合条件的位置

1.查找第一个≥key的元素的位置

设a为单调不减整数系列,在系列a中查找第一个≥key的数的位置。

int left=1,right=n+1;//为什么right的初始值为n+1,因为存在着key比所有元素都大的情况
int lower_bound(int a[],int left,int right,int key){
       int mid;
       while(left<right){
              mid=(left+right)/2;
              if(a[mid]>=key) right=mid;
              else left=mid+1;
       }
       return left;
}

2.查找最后一个≤key的元素的位置

  设系列a为单调不减整数系列,在系列a中查找最后一个≤key的数的位置。

int left=0,right=n;//left和right边界初始区间为[0,n],因为key有可能比所有的数都更小
int last_bound(int a[],int left,int right,int key){
	int mid;
	while(left<right){
		mid=(left+right+1)/2;//为什么还要多加1呢??
		if(a[mid]<=key) left=mid;
		else right=mid-1;
	}
	return left;
}

  第二种模板比较复杂,它涉及到两种缩小区间的形式。

形式一:right=mid,left=mid+1,取中间值时mid=(left+right)/2;

形式二:left=mid,right=mid-1,取中间值时mid=(left+right+1)/2;

三、总结

为什么第二种查找的循环跳出条件是left<right,为什么不是≤呢?因为我们的区间变换思路是不断的舍去不可能是解的区间,最后只剩下一个数就是我们的解。而第一种情况就算最后只剩一个数也有可能不是解,所以需要使用小于等于。

  • 查找精确值,循环条件是小于等于;查找满足情况的最大最小值,循环条件是小于。
  • 查找满足条件的最大数,mid = (right + left + 1) / 2;查找满足条件的最小数,mid = (right + left)/2
  • mid = left + (right - left) / 2,不是适用于所有的情况。

作者:T-star
来源链接:https://www.acwing.com/blog/content/307/

posted @ 2019-07-24 19:38  蒟蒻教练  阅读(340)  评论(0编辑  收藏  举报