二分查找
Search a value
int binarySearch(int []nums,int tar){
int l=0,r=nums.length-1;
while(l<=r){
int mid=l+(r-l)/2;//l+r>>1
if(nums[mid]==tar) return mid;
return mid;
else if(nums[mid]<tar) l=mid+1;
else if(nums[mid]>tar) r=mid-1;
}
return -1;
}
while循环的条件为<=时为闭区间,终止条件为left=right+1
<为左开右闭区间,终止条件为left=right。变成<=只需打个补丁:
while(left<right){
//...
}
return a[left]==target?left:-1;
```
#### 缺陷
nums=\[1,2,2,2,2,2,3] 无法处理,只是找到
### 寻找左侧边界
```c++
if(nums.length==0) return -1;
l=0,r=num.length;
while(l<r)
//...
if(nums[mid]==tar) r=mid;
else if(nums[mid]<tar) l=mid+1;
else if(nums[mid]>tar) r=mid
return left;
找到tar后->搜索[left,right).
left 的特殊含义:nums中小于tar的元素有left个,
所以仅需添加
while(l<r){
//...
}//比所有数都大
if(l==nums.length) return -1;
return nums[l]==tar?l:-1;
适用于
- 数组有序,包含重复元素
- 数组部分有序,不包含重复元素
另一种
对右侧值的收缩更保守
l=0,r=nums.length-1;
while(l<r){
//...
if(nums[mid]<tar) l=mid+1;
else if(nums[mid]>tar) r=mid;
else r--;
}
return nums[l]==tar?l:-1;
寻找右侧边界
//同左侧边界
if(nums[mid]==tar) l=mid+1;
//同左侧边界
/*return l-1;*/
if(l==0) return -1;//l-1
return nums[l-1]==tar?(l-1):-1;
事实上,return left与right 无异;
“当 nums[mid] == target 时,不要立即返回,而是增大「搜索区间」的下界 left,使得区间不断向右收缩,达到锁定右侧边界的目的。”
while 循环结束时,nums[left] 一定不等于 target 了,而 nums[left - 1]可能是target。
二分查找局部极大值
l=0,r=nums.length-1;
while(l<r){
mid=l+((r-l)>>1);
if(nums[mid]<nums[mid+1]) l=mid+1;
else r=mid;
}
return l;
写在后面
| 查找方式 | 循环条件 | 左侧更新 | 右侧更新 | 中间点位置 | 返回值 |
|---|---|---|---|---|---|
| 标准二分查找 | left <= right | left = mid - 1 | right = mid + 1 | (left + right) / 2 | -1 / mid |
| 二分找左边界 | left < right | left = mid - 1 | right = mid | (left + right) / 2 | -1 / left |
| 二分找右边界 | left < right | left = mid | right = mid - 1 | (left + right) / 2 + 1 | -1 / right |
二分查找变种
l=0,r=nums.length-1;
while(l<=r){
if(nums[mid] ? tar) r=mid-1;
else l=mid+1;
}
return ...;
- 判断出是返回left or right:上例中,r=l-1时终止,比较值为tar,查找小于等于(或者是小于)tar的元素,则边界值就是等于key的所有元素的最左边的,返回left。
- 判断比较符号:查找小于等于时,用>=此时r=mid-1。
PS:
lower_bound(val) 找的是 x>=val的下界,last则不存在;
upper_bound(val) 找 x>val的下界;
因为区间离散,-1后为对应上界。

浙公网安备 33010602011771号