一、二分查找
1.例题
二分查找(简单)
2.实现
class Solution {
public int search ( int [ ] nums, int target) {
int left = 0 ;
int right = nums. length - 1 ;
while ( left <= right) {
int mid = left + ( right - left) / 2 ;
if ( nums[ mid] == target) {
return mid;
} else if ( nums[ mid] < target) {
left = mid + 1 ;
} else if ( nums[ mid] > target) {
right = mid - 1 ;
}
}
return - 1 ;
}
}
3.解析
根据题目提示信息,本题不需要考虑数组为空。 right的值取 nums.length - 1,表示搜索区间为[left, right],当“left == right + 1”(相当于[right + 1, left]),此时区间没有值,不需要再进行比较,因此跳出循环的条件应该为left <= right。 left + (right - left) / 2 相当于 (right + left) / 2,但是前者能够有效防止left
和right
的值太大且直接相加导致溢出。 当中间值nums[mid] 等于目标值时,返回数组下标(本题元素不重复)。 当中间值小于目标值时,则去右半边的数组中继续搜索,左指针left = mid + 1,同理,大于目标值时,right = mid - 1(本题数组是升序)。
二、寻找左右边界的二分查找
1.例题
在排序数组中查找元素的第一个和最后一个位置(中等)
2.实现
class Solution {
public int [ ] searchRange ( int [ ] nums, int target) {
return new int [ ] { left_bound ( nums, target) , right_bound ( nums, target) } ;
}
private int left_bound ( int [ ] nums, int target) {
if ( nums. length == 0 )
return - 1 ;
int left = 0 ;
int right = nums. length - 1 ;
while ( left <= right) {
int mid = left + ( right - left) / 2 ;
if ( nums[ mid] == target) {
right = mid - 1 ;
} else if ( nums[ mid] > target) {
right = mid - 1 ;
} else if ( nums[ mid] < target) {
left = mid + 1 ;
}
}
if ( left >= nums. length || nums[ left] != target)
return - 1 ;
return left;
}
private int right_bound ( int [ ] nums, int target) {
if ( nums. length == 0 )
return - 1 ;
int left = 0 ;
int right = nums. length - 1 ;
while ( left <= right) {
int mid = left + ( right - left) / 2 ;
if ( nums[ mid] == target) {
left = mid + 1 ;
} else if ( nums[ mid] > target) {
right = mid - 1 ;
} else if ( nums[ mid] < target) {
left = mid + 1 ;
}
}
if ( right < 0 || nums[ right] != target)
return - 1 ;
return right;
}
}
3.解析
当nums[mid] == target时,不能直接返回,而是缩小边界继续搜索。 最后要判断left 和 right的值,防止数组越界异常。 返回值为什么是left 或者 right? 这里以左边界举例,right变化的条件是nums[mid] >= target。 如图所示,搜索过程中右边界收缩,当right
停止变化时,即nums[right1] == target,且right1是目标值的左边界下标
,此时left
会往右走,一直到left == right2
,最终left = right2 + 1 = right1
,所以返回值是left
。 当nums[right1] > target,且nums[mid] < target
时,left 会往右移动,如果数组中不存在target
时,left 最终也会等于新的right
。 (右边界思路同左边界一样)