Binary Search 二分查找
在有序数组中(允许重复数字),查找第一个或最后一个目标数字。时间复杂度为O(logN)。如果需要优化O(n)的时间复杂度,那么只能是O(logn)的二分法。
基本模板:
0. 查找一个(原始方法)
private static int bs(int[] nums, int target) {
int start = 0;
int end = nums.length - 1;
int mid;
while (start <= end) {
mid = start + ((end - start) >> 1);
if (target < nums[mid]) {
end = mid - 1;
} else if (target > nums[mid]) {
start = mid + 1;
} else {
return mid;
}
}
return -1;
}
1. 查找第一个
循环方法:
public static int firstBS(int[] nums, int target) {
if (nums == null || nums.length == 0) {
return -1;
}
int start = 0;
int end = nums.length - 1;
int mid;
while (start + 1 < end) {
mid = (start + ((end - start) >> 1));
if (target < nums[mid]) {
end = mid;
} else if (target > nums[mid]) {
start = mid;
} else {
end = mid;
}
}
if (nums[start] == target) {
return start;
} else if (nums[end] == target) {
return end;
} else {
return -1;
}
}
递归方法:
public static int firstBS(int[] nums, int target, int start, int end) {
if (nums == null || nums.length == 0) {
return -1;
}
if (start + 1 < end) {
int mid = start + ((end - start) >> 1);
if (target < nums[mid]) {
return firstBS(nums, target, start, mid);
} else if (target > nums[mid]) {
return firstBS(nums, target, mid, end);
} else {
return firstBS(nums, target, start, mid);
}
} else {
if (nums[start] == target) {
return start;
} else if (nums[end] == target) {
return end;
} else {
return -1;
}
}
}
2. 查找最后一个
循环方法:
public static int lastBS(int[] nums, int target) {
if (nums == null || nums.length == 0) {
return -1 ;
}
int start = 0;
int end = nums.length - 1;
int mid;
while (start + 1 < end) {
mid = (start + ((end - start) >> 1));
<span style="white-space:pre"> </span>if (target < nums[mid]) {
end = mid;
} else if (target > nums[mid]) {
start = mid;
} else {
start = mid;
}
}
if (nums[end] == target) {
return end;
} else if (nums[start] == target) {
return start;
} else {
return -1;
<span style="white-space:pre"> </span>}
}
递归方法:
public static int lastBS(int[] nums, int target, int start, int end) {
if (nums == null || nums.length == 0) {
return -1;
}
if (start + 1 < end) {
int mid = start + ((end - start) >> 1);
if (target < nums[mid]) {
return lastBS(nums, target, start, mid);
} else if (target > nums[mid]) {
return lastBS(nums, target, mid, end);
} else {
return lastBS(nums, target, mid, end);
}
} else {
if (nums[end] == target) {
return end;
} else if (nums[start] == target) {
return start;
} else {
return -1;
}
}
}
总结:
1. 循环方法和递归方法结构上类似:
- 判断边界条件
- 判断 start + 1 < end
- 如果 target < nums[mid],则 end = mid。 慎重使用 end = mid - 1
- 如果 target > nums[mid],则 start = mid。慎重使用 start = mid + 1
- 如果 target == nums[mid],找第一个 end = mid ;找最后一个 start = mid。这一步可以和上面合并在一起。
- 判断 nums[start] 和 nums[end] 是否是要找的目标数字。找第一个先判断 nums[start],找最后一个先判断 nums[end]
题目汇总:
1. Binary Search 查找第一个目标数字
2. Search Insert Position 查找第一个目标数字,最后根据位置判断。
3. Search for a Range 查找第一个和最后一个目标数字。
4. Search in Rotated Sorted Array 在旋转数组中找到目标数字,画图分析,二分排除。
5. Search in Rotated Sorted Array II 在含重复数字的旋转数组中找目标数字,无法基于二分查找进行优化,数组可看为无序,{2,2,3,2,2}, {3,2,2,2,3}。遍历查找即可。
6. Find Minimum in Rotated Sorted Array 在旋转数组中找到最小值,画图分析,二分排除,将转折点缩小范围到start 和 end。或者暴力求解。
7. Find Minimum in Rotated Sorted Array II 在含重复数字的旋转数组中找目标数字,无法基于二分查找进行优化,数组可看为无序。遍历查找即可。
8. First Bad Version 在boolean有序数组中找到第一个true,直接二分查找即可。
9. Find Peak Element 在多旋转数组中找一个波峰,只找mid,不用加减1。循环外要return。
10. Search a 2D Matrix 在矩阵中搜索目标数字。可以用多行二分搜索。或者右上角/左下角搜索。
11. Search a 2D Matrix II 在矩阵中搜索目标数字的出现次数。可以用多行二分搜索。或者右上角/左下角搜索。
12. Sqrt(X) 求平方根。用二分搜索。注意乘法会溢出,改用除法。
13. Median of two Sorted Arrays 根据两个数组,求中位数。二分排除法。