Appearance
二分查找(折半查找)
Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky...
——Knuth
二分查找又称折半查找,是将序列中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将序列分成前、后两个子序列,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子序列,否则进一步查找后一子序列。重复以上过程,直到找到满足条件的记录,则查找成功,或直到子序列不存在为止,此时查找不成功。
三种应用场景:寻找一个数、寻找左侧边界、寻找右侧边界。
一、寻找一个数
本题使用二分查找的前提条件:
- 数组为有序数组
- 数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的
写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。
1. 左闭右闭
class Solution {
public:
int search(vector<int>& nums, int target) {
int l=0,r=nums.size()-1;
while(l<=r){
int mid=l+(r-l)/2; // 避免溢出
if(nums[mid]>target) r=mid-1;
else if(nums[mid]<target) l=mid+1;
else return mid;
}
return -1;
}
};
喜欢左闭右闭的写法。
2. 左闭右开
class Solution {
public:
int search(vector<int>& nums, int target) {
int l=0,r=nums.size();
while(l<r){
int mid=l+(r-l)/2;
if(nums[mid]>target) r=mid;
else if(nums[mid]<target) l=mid+1;
else return mid;
}
return -1;
}
};
二、寻找左侧边界
class Solution {
public:
int left_bound(vector<int>& nums, int target) {
int l=0,r=nums.size();
while (l<r) {
int mid=l+(r-l)/2;
if(nums[mid]<target) l=mid+1;
else r=mid;
}
return l;
}
}
等于时改变r。
三、寻找右侧边界
class Solution {
public:
int right_bound(vector<int>& nums, int target) {
int l=0,r=nums.size();
while (l<r) {
int mid=l+(r-l)/2;
if(nums[mid]<=target) l=mid+1;
else r=mid;
}
return l-1;
}
}
等于时改变l。