1.搜索二维矩阵
- 注意要求代码的时间复杂度为 O(log(m * n))
- 对于整体的数组进行二分查找,在定位到数组中的位置
java
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int left = 0;
int right = m * n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (matrix[mid / n][mid % n] == target) {
return true;
}
if (matrix[mid / n][mid % n] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
2.在排序数组中查找元素的第一个和最后一个位置
- 题目要求时间复杂度是 O(log n)
- 进行两次二分查找:寻找两个边界
- 第一次二分:找最左边的 target
- 第二次二分:找最右边的 target
java
public int[] searchRange(int[] nums, int target) {
int start = findStart(nums, target);
int end = findEnd(nums, target);
return new int[]{start, end};
}
//寻找左端点
private int findStart(int[] nums, int target) {
int ans = -1;
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
ans = mid;
right = mid - 1;
}
if (nums[mid] < target) {
left = mid + 1;
}
if (nums[mid] > target) {
right = mid - 1;
}
}
return ans;
}
//寻找右端点
private int findEnd(int[] nums, int target){
int ans = -1;
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
ans = mid;
left = mid + 1;
}
if (nums[mid] < target) {
left = mid + 1;
}
if (nums[mid] > target) {
right = mid - 1;
}
}
return ans;
}
简化后的代码
java
public int[] searchRange(int[] nums, int target) {
int first = lowerBound(nums, target);
// 没找到 target
if (first == nums.length || nums[first] != target) {
return new int[]{-1, -1};
}
int last = lowerBound(nums, target + 1) - 1;
return new int[]{first, last};
}
// 找第一个 >= target 的位置
private int lowerBound(int[] nums, int target) {
int left = 0;
int right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
3.搜索旋转排序数组⭐
- 每次通过 mid 进行二分查找的时候,得到的结果要么 左半部分有序右半部分无序 或者 左半部分无序右半部分有序
- nums 中的每个值都 独一无二,因此不存在重叠范围的问题
- 可以通过 left、mid、right 的值判断左右有序的数组,如果 target 在有序数组中则直接二分查找,否则在递归进行判断
bash
下标: 0 1 2 3 4 5 6
数组:[4,5,6,7,0,1,2]
↑
mid
java
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;
}
//判断是否左有序
if (nums[left] <= nums[mid]) {
//判断target是否在左有序区间
if (target >= nums[left] && target <= nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
//判断target是否在右有序区间
if (target >= nums[mid] && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
4.寻找旋转排序数组中的最小值
- 最小值其实就是两个有序部分的分界点。
- 同样二分查找中,min将数组分为左右两侧,左有序右无序或左五序右有序
- 无序的一侧则继续递归
java
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right]) {
// 最小值一定在 mid 右边
left = mid + 1;
} else {
// 最小值可能是 mid,也可能在 mid 左边
right = mid;
}
}
return nums[left];
}