二分主要用于降低时间复杂度,可以将 O(n) 复杂度降低为 O(logn)
二分模板
版本一:
int search_1(int l, int r) {
while(l < r) {
int mid = (l + r) >> 1;
if (判断条件) r = mid;
else l = mid + 1;
}
return l;
}
版本二:
int search_2(int l, int r) {
while(l < r) {
int mid = (l + r + 1) >> 1;
if (判断条件) l = mid;
else r = mid - 1;
}
return l;
}
总结:
- 这里 mid + 1 或 mid - 1 是将 mid 归为 另外一半,mid 不可能同时处于两边
- 版本一和版本二主要区别是:版本一处理结果是得到目的值最小情况,版本二是处理结果是得到目的值最大的情况:例如给定排序数组 [5,7,8,8,8,10] 获得 target = 8 在数组中的第一个和最后一个位置的下标,第一个模板可以返回最小下标即 2,第二个模板返回最大下标 4
- 注意版本二中 int mid = (l + r + 1) >> 1; 是为了避免死循环
例题一:
有效的完全平方数:给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。
说明:不要使用任何内置的库函数,如 sqrt。
class Solution {
public boolean isPerfectSquare(int num) {
int l = 1, r = num / 2;
while (l < r) {
int mid = (l + r) >> 1;
if (mid >= num / mid) r = mid;
else l = mid + 1;
}
return l*l == num;
}
}
例题二:
在排序数组中查找元素的第一个和最后一个位置 : 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。 你的算法时间复杂度必须是 O(log n) 级别。 如果数组中不存在目标值,返回 [-1, -1]。
从这个题目能够看出版本一和版本二的主要区别
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums == null || nums.length == 0 || target < nums[0] || target > nums[nums.length - 1]) return new int[]{-1, -1};
// 二分查找优化算法
int l = 0, r = nums.length - 1;
int start;
// 大于等于最小的
while(l < r) {
int mid = (l + r) >> 1;
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
// 二分查找一次,如果找不到说明这个数组中没有该元素
if (nums[l] != target) return new int[]{-1,-1};
else start = l;
l = 0;
r = nums.length - 1;
// 小于等于最大的
while(l < r) {
int mid = (l + r + 1) >> 1;
if (nums[mid] <= target) l = mid;
else r = mid - 1;
}
return new int[]{start, l};
}
}
例题三:
寻找峰值:峰值元素是指其值大于左右相邻值的元素。给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。 数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。 你可以假设 nums[-1] = nums[n] = -∞。
主要解题思想:如果给定一个 nums[mid] 不为峰值的话,那么必然有峰值 nums[mid] 中(保证这个条件成立的是数组的边界是 -∞)
class Solution {
public int findPeakElement(int[] nums) {
if (nums.length == 1 || nums[0] > nums[1]) return 0;
int l = 0, r = nums.length - 1;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (nums[mid - 1] < nums[mid]) l = mid;
else r = mid - 1;
}
return l;
}
}