今日分享 整数二分
1. 二分法的核心思想
二分法就像猜数字游戏。每次猜中间数,根据大小提示排除一半答案。
这种方法将 O(n) 的线性查找,优化到 O(log n) 的对数级别。
2. 整数二分的"奥义"
整数二分的精髓在于边界处理。因为整数没有"中点",必须明确:
查找区间是 [left, right] (闭区间)
每次排除时,边界如何更新才不会漏掉答案
处理不好边界,就会导致死循环或漏掉正确答案。
3. 两种经典模板与技巧
根据要找的目标不同,分为两种模板:
模板一:查找第一个满足条件的值
用于查找"左边界",例如:数组中第一个大于等于 x 的数。
int findFirst(int *nums, int n, int x) {
int left = 0, right = n - 1;
int ans = n; // 初始化为一个不可能的位置
while (left <= right) {
int mid = left + (right - left) / 2; // 防止溢出
if (nums[mid] >= x) { // 满足条件,尝试找更左边的
ans = mid;
right = mid - 1;
} else { // 不满足,去右边找
left = mid + 1;
}
}
return ans;
}
模板二:查找最后一个满足条件的值
用于查找"右边界",例如:数组中最后一个小于等于 x 的数。
int findLast(int *nums, int n, int x) {
int left = 0, right = n - 1;
int ans = -1; // 初始化为一个不可能的位置
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= x) { // 满足条件,尝试找更右边的
ans = mid;
left = mid + 1;
} else { // 不满足,去左边找
right = mid - 1;
}
}
return ans;
}
4. 解题技巧总结
1. 确定区间:永远使用闭区间 [left, right] ,逻辑最清晰。
2. 循环条件:使用 left <= right ,保证退出时所有元素都被检查。
3. 计算中点:用 mid = left + (right - left) / 2 避免 left + right 溢出。
4. 边界移动:根据"找左边界"还是"找右边界"选择移动 left 或 right 。
5. 验证答案:退出循环后,记得检查 ans 是否在合理范围内。
例题实战:在排序数组中查找元素的第一个和最后一个位置
题目描述:
给定一个按照升序排列的整数数组 nums ,和一个目标值 target 。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值,返回 [-1, -1] 。
示例:
输入: nums = [5,7,7,8,8,10] , target = 8
输出: [3, 4]
C 语言实现
#include <stdio.h>
#include <stdlib.h>
int* searchRange(int* nums, int numsSize, int target, int* returnSize) {
*returnSize = 2;
int* result = (int*)malloc(sizeof(int) * 2);
result[0] = -1;
result[1] = -1;
// 找左边界
int left = 0, right = numsSize - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
if (left < numsSize && nums[left] == target) {
result[0] = left;
} else {
return result;
}
// 找右边界
left = 0;
right = numsSize - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
result[1] = right;
return result;
}
int main() {
int nums[] = {5,7,7,8,8,10};
int target = 8;
int returnSize;
int* result = searchRange(nums, 6, target, &returnSize);
printf("[%d, %d]\n", result[0], result[1]);
free(result);
return 0;
}
C++ 实现
#include <iostream>
#include <vector>
using namespace std;
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> result(2, -1);
int n = nums.size();
if (n == 0) return result;
// 找左边界
int left = 0, right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
if (left < n && nums[left] == target) {
result[0] = left;
} else {
return result;
}
// 找右边界
left = 0;
right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
result[1] = right;
return result;
}
int main() {
vector<int> nums = {5,7,7,8,8,10};
int target = 8;
vector<int> res = searchRange(nums, target);
cout << "[" << res[0] << ", " << res[1] << "]" << endl;
return 0;
}
Python 实现
def searchRange(nums, target):
result = [-1, -1]
n = len(nums)
if n == 0:
return result
# 找左边界
left, right = 0, n - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] >= target:
right = mid - 1
else:
left = mid + 1
if left < n and nums[left] == target:
result[0] = left
else:
return result
# 找右边界
left, right = 0, n - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] <= target:
left = mid + 1
else:
right = mid - 1
result[1] = right
return result
nums = [5,7,7,8,8,10]
target = 8
print(searchRange(nums, target))
Java 实现
import java.util.Arrays;
public class SearchRange {
public static int[] searchRange(int[] nums, int target) {
int[] result = {-1, -1};
int n = nums.length;
if (n == 0) return result;
// 找左边界
int left = 0, right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
if (left < n && nums[left] == target) {
result[0] = left;
} else {
return result;
}
// 找右边界
left = 0;
right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
result[1] = right;
return result;
}
public static void main(String[] args) {
int[] nums = {5,7,7,8,8,10};
int target = 8;
int[] res = searchRange(nums, target);
System.out.println(Arrays.toString(res));
}
}