
本文内容如下:
① 详述了作者对二分查找算法的通俗理解
② 以经典二分查找题为例,进行了代码示例分享
零、例题链接
二分查找
一、使用条件
- 含有n个元素的nums数组元素值非单调递减
二、核心思想
- 循环:将target与区间内中间位置值比较,对半缩小可能包含target的区间长度【比较大小后,target在区间左/右半边的概率为0】
三、算法关键步骤
- 第一步
- 设置left、right索引初始值
- left、right索引初始值设置的不同,决定了可能包含target的区间形式
- 可能包含target的区间形式有四种
- 左开右开
- left和right均为开区间边界,即target可能在区间内,但一定不是left和right指向的元素(或target可能所在的数组索引一定不是left和right)。
- 左闭右闭【常用】
- left和right均为闭区间边界,即target可能在包含 “left和right指向的元素” 的区间内
- 左开右闭
- target可能在包含 “right指向的元素” 的区间内,但left指向的元素一定不是target
- 左闭右开【常用】
- target可能在包含 “left指向的元素” 的区间内,但right指向的元素一定不是target
- 第二步
- 判断while循环条件
- 循环条件的选取,取决于可能包含target的区间形式
- 左开右开:left < right - 1
- 当left = right - 1时,可能包含target的区间内元素数量为0
- 左闭右闭:left <= right
- 当left > right时,可能包含target的区间内元素数量为0
- 左开右闭:left < right
- 当left = right时,可能包含target的区间内元素数量为0
- 左闭右开:left < right
- 当left = right时,可能包含target的区间内元素数量为0
- 第三步
- 进入循环后,只需要保证两点
- 一
- 无论计算前后,保证区间内的元素都没被判断过【这决定了left、right重赋值的方式】
- 重点注意:计算后,nums[ mid ]属于被判断过的元素
- 二
- 无论向上/向下取整计算mid索引,保证mid索引始终指向区间内的某一元素【这决定了mid的计算方式】
四、代码示例
class Solution {
public:
int search(vector<int>& nums, int target) {
//情况一:左开右开
/***********************重点注意***********************/
int left1 = -1, right1 = nums.size(); //决定了可能包含target的区间形式为:左开右开
/*****************************************************/
int mid1 = 0;
while(left1 < right1 - 1) {
mid1 = left + (right - left) / 2; //防止加法溢出
if(nums[mid1] > target)
right1 = mid1;
else if(nums[mid1] == target)
return mid1;
else if(nums[mid1] < target)
left1 = mid1;
}
//情况二:左闭右闭
/***********************重点注意***********************/
int left2 = 0, right2 = nums.size() - 1; //决定了可能包含target的区间形式为:左闭右闭
/*****************************************************/
int mid2 = 0;
while(left2 <= right2) {
mid2 = (left2 + right2) / 2;
if(nums[mid2] > target)
right2 = mid2 - 1;
else if(nums[mid2] == target)
return mid2;
else if(nums[mid2] < target)
left2 = mid2 + 1;
}
//情况三:左闭右开
/***********************重点注意***********************/
int left3 = 0, right3 = nums.size(); //决定了可能包含target的区间形式为:左闭右开
/*****************************************************/
int mid3 = 0;
while(left3 < right3) {
mid3 = (left3 + right3) / 2;
if(nums[mid3] > target)
right3 = mid3;
else if(nums[mid3] == target)
return mid3;
else if(nums[mid3] < target)
left3 = mid3 + 1;
}
//情况四:左开右闭:向上取整
//使用向上取整的原因:向下取整可能会将mid指向左开区间边界值,又因为我们限定左开右闭,即mid不可指向左边界值,因此mid的指向矛盾,故采用向上取整。
/***********************重点注意***********************/
int left4 = -1, right4 = nums.size() - 1; //决定了可能包含target的区间形式为:左开右闭
/*****************************************************/
float temp = 0.0;
int mid4 = 0;
while(left4 < right4) {
temp = left4 + right4;
mid4 = ceil(temp / 2); //ceil():向上取整
if(nums[mid4] > target)
right4 = mid4 - 1;
else if(nums[mid4] == target)
return mid4;
else if(nums[mid4] < target)
left4 = mid4;
}
return -1;
}
};
五、相关习题
- 34.在排序数组中查找元素的第一个和最后一个位置