目录
1. 为什么是 while(left <= right), 而不是 while(left < right)
2. 为什么 left = mid + 1 和 right = mid - 1
一、二分查找框架
int binarySearch(int[] nums, int target) {
int left = 0, right = ...;
while(...) {
int mid = left + (right - left) / 2; // 防止溢出
if (nums[mid] == target) {
...
} else if (nums[mid] < target) {
left = ...
} else if (nums[mid] > target) {
right = ...
}
}
return ...;
}
二、寻找一个数
int binarySearch(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;
}
讨论细节:
1. 为什么是 while(left <= right), 而不是 while(left < right)
因为 right 初值是 nums.lenght - 1,即最后一个数的索引。搜索区间为 [left, right] 双闭区间。
- left <= right 的终止条件是 left == right + 1, 终止搜索区间为 [right + 1, right] 。
- left < rihgt 的终止条件是 left == right, 终止搜索区间为 [right, right] 。区间非空,right 这个值还没被搜索,因此不合理。
2. 为什么 left = mid + 1 和 right = mid - 1
因为 mid 已被搜索过,下次搜索 [mid + 1, right] 或 [left, mid - 1]。
3. 此算法缺陷
nums = [1,2,2,2,3] 这种找2,会返回索引2,不会找到边界索引1或者3。
三、寻找左侧边界或右侧边界
三种写在一起便于比较理解:
int binary_search(int[] nums, int target) {
int left = 0, 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) {
// 直接返回
return mid;
}
}
// 直接返回
return -1;
}
int left_bound(int[] nums, int target) {
int left = 0, 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) {
// 别返回,锁定左侧边界
right = mid - 1;
}
}
// 最后要检查 left 越界的情况
if (left >= nums.length || nums[left] != target)
return -1;
return left;
}
int right_bound(int[] nums, int target) {
int left = 0, 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;
}
}
// 最后要检查 right 越界的情况
if (right < 0 || nums[right] != target)
return -1;
return right;
}
寻找左右边界:
- 就在 nums[mid] == target 时不直接返回,例如,寻找左边界时让 right = mid - 1,使区间不断向左收缩。
- 最后要检测数值越界情况。