图解 | 二分查找法需要注意的那些细节
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4思路分析 关于二分查找法维基百科给出的定义是: 二分查找法是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。 这里我们先给出二分查找法的第一种写法:
public int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right-left)/2;
if (target == nums[mid]) {
return mid;
} else if (target < nums[mid]){
right = mid - 1;
} else if (target > nums[mid]){
left = mid + 1;
}
}
return -1;
}
接着对代码中需要注意的几个细节进行说明:
一是程序什么时候停止?
在上述代码中的第2行和第3行,分别定义了left=0,right=nums.length-1。这表明我们是在左闭右闭的区间[left, right]中查找目标值。
int left = 0;
int right = nums.length - 1;
既然是查找目标值,那么就有找到和找不到的情况。
对于能找到目标值的情况,只需直接将目标值返回即可,代码如下:
int mid = left + (right-left)/2;
if (target == nums[mid]) {
return mid;
}
对于折半查找但最终没找到的情况,根据题意是返回-1即可。那么,这时程序什么时候停止呢?由于这里定义的是在[left, right]这个左闭右闭的区间内查找,因此当left=right时,区间[left, right]依然包含一个有效的待查找元素。所以在不存在目标值的情况下,程序终止的条件是left>right。
while (left <= right) {
// 查找逻辑
}
二是当target小于nums[mid]时,为什么right=mid-1呢?
if (target < nums[mid]){
right = mid - 1;
}
首先,target小于nums[mid],意味着接下来应该在左半边进行查找,所以要明确右边界right的值。
而这里定义的查找区间是[left, right]这个左闭右闭的区间,又,mid已经确认比target大了,因此right=mid-1。
三是当target大于nums[mid]时,为什么left=mid+1呢?
if (target > nums[mid]){
left = mid + 1;
}
同样的,当target大于nums[mid]时,意味着接下来应该去右半边进行查找,所以要明确左边界left的值。
又,我们是在[left, right]这个左闭右闭的区间内查找且已经明确target大于nums[mid]了,所以left=mid+1。
在理解了第一种写法后,接着看下第二种写法。第二种写法与第一种写法的一个主要区别是查找区间变为了左闭右开[left, right)。
这一变化使得代码相比第一种写法有两处不同:
一是while循环继续的条件变为了left<right。因为如果left=right,那么区间[left,right)是不存在的,比如区间[2,2)。
while (left < right) {
// 查找逻辑
}
二是当target小于nums[mid]时,右侧边界right=mid而不是之前的mid-1。原因还是由于这里定义的查找区间是[left, right),那么当target<nums[mid]时,nums[mid-1]还是需要考察的,所以right=mid。
if (target < nums[mid]){
right = mid;
}
具体的代码实现如下,可参考详细注释进行理解。
最后对于LeetCode 704这个题目,制作了详细动画演示:
今天的分享就到这里了 错误或不足之处 欢迎留言指出 下一篇我们将学习新的内容,敬请期待