Leetcode Link:二分查找
代码随想录:超详解
题目原文
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果 target 存在返回下标,否则返回 -1。
你必须编写一个具有 O(log n) 时间复杂度的算法。
提示:
- 你可以假设
nums中的所有元素是不重复的。 n将在[1, 10000]之间。nums的每个元素都将在[-9999, 9999]之间
分析
- 给定的数据不重复且有序排列
- 时间复杂度要求为对数级(一般来说“砍成几半”是最容易想到的关于对数级复杂度的方向)
- 返回值为某数组元素的下标或
-1
结论:本题可以使用二分查找法,不能使用一般的暴力穷举法(O(n))
题解与要点
One possible Answer:左闭右开 [a, b)
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size();//要点①
while(left < right){ // 要点②
int middle = left + ((right - left) / 2); // 要点③
if(nums[middle] > target){
right = middle; // 要点④
}
else if(nums[middle] < target){
left = middle + 1; // 要点⑥
}
else {
return middle;
}
}
return -1;
}
};
要点
①左右边界:左侧边界为下标0的元素(区间包含此元素);右侧边界指向“越界”的nums[nums.size()]元素,意为开区间,实际上用不到。
注:此处右边界不可以写为right = nums,size() - 2,这个语句实际上指向区间内的nums[nums.size() - 2],会导致右侧边界错误缩小,最后一个元素被排除在搜索区间之外。
②循环条件:因为使用左闭右开区间抽象约束“循环中不变的那一块区域,一直被囊括在搜索区间的那块区域(循环不变量)”,当left == right时right并不在搜索区间内,所以不适用<=。
③middle中间值索引的计算:left + ((right - left) / 2)等价于middle = (right + left)/2,采用前者的原因是防止索引值过大,超出数据类型所规定的最大值。
例如:假设 int 类型最大值是 1000,而 left = 600,right = 800
- 方法1:(600 + 800) / 2 = 1400 / 2 = 700,但在计算 600+800 时就已经溢出了
- 方法2:600 + ((800 - 600) / 2) = 600 + (200 / 2) = 600 + 100 = 700,不会溢出
④右边界迭代:中间值大于目标值,应当缩小右边界。尽管middle不是目标值target,但由于右边界为开区间,right = middle并未将middle包含在下一次的搜索区间内。
⑤左边界迭代:中间值小于目标值,应当缩小左边界。由于左边界为闭区间,需避免将不合要求的中间值middle囊括在区间内,因此middle+1。
1124

被折叠的 条评论
为什么被折叠?



