一般用法:
public int binarySearch(int[] nums, int key) {
int l = 0, h = nums.length - 1;
while (l <= h) {
int m = l + (h - l) / 2; // 防止l+h越界
if (nums[m] == key) {
return m;
} else if (nums[m] > key) {
h = m - 1;
} else {
l = m + 1;
}
}
return -1;
}
二分法的时间复杂度为O(logN)
变种
例如在一个有重复元素的数组中查找 key 的最左位置的实现如下:
public int binarySearch(int[] nums, int key) {
int l = 0, h = nums.length - 1;
while (l < h) {
int m = l + (h - l) / 2;
if (nums[m] >= key) {
h = m;
} else {
l = m + 1;
}
}
return nums[l] == key ? l : -1;
}
注意:
l < h 而不是 l <= h,如果写成 l <= h 会出现死循环。对 mid 的计算是偏左的,如 (3, 4)的 mid 是 3,右边界不断向左靠近,当 l 等于 右边界 h 时,代表右边界向左移动到极限。
LeetCode 69. Sqrt(x)
public int mySqrt(int x) {
if(x <= 1) return x;
int l = 1, h = x;
while(l <= h) {
int mid = l+(h-l)/2;
int sqrt = x/mid; // mid*mid 可能越界
if(sqrt == mid) return mid;
else if (sqrt > mid) l = mid+1;
else h = mid - 1;
}
return h;
}
744. Find Smallest Letter Greater Than Target
大于给定元素的最小元素
public char nextGreatestLetter(char[] letters, char target) {
int l = 0, h = letters.length;
while(l < h) {
int mid = l+(h-l)/2;
if(letters[mid] > target) h = mid;
else l = mid+1;
}
return l == letters.length ? letters[0] : letters[l];
}
540. Single Element in a Sorted Array
有序数组的 Single Element
第一个版本:
public int singleNonDuplicate(int[] nums) {
if(nums.length == 1) return nums[0];
int l = 0, h = nums.length-1;
while(l <= h) {
int mid = l+(h-l)/2;
if(mid == 0 || mid == nums.length-1) return nums[mid];
else if(nums[mid] != nums[mid-1]) {
if(nums[mid]!=nums[mid+1]) return nums[mid];
else {
if((mid & 1) == 1) {
h = mid-1;
}else {
l = mid+2;
}
}
}else {
if((mid & 1) == 1) {
l = mid+1;
}else{
h = mid-2;
}
}
}
return -1;
}
解题思路:求得 mid 与所有比较,若都不相同则返回nums[mid],否则根据mid的位置来判断单个元素在左侧还是右侧。注意边界值,当mid == 0 或 mid == length-1时直接返回nums[mid]。
我们来对上面代码进行优化:
// 优化:利用数组的特点:通过主动选择mid,确保其为偶数,这样其左侧的元素个数就是偶数
public int singleNonDuplicate2(int[] nums) {
int l = 0, h = nums.length-1;
while(l < h) {
int mid = l+(h-l)/2;
if((mid & 1) == 1) mid--;
if(nums[mid] == nums[mid+1]) l = mid+2;
else h = mid;
}
return nums[h];
}
153. Find Minimum in Rotated Sorted Array
旋转数组的最小数字
public int findMin(int[] nums) {
int l = 0, h = nums.length-1;
while(l < h) {
int mid = l+(h-l)/2;
if(nums[mid] < nums[h]) h = mid;
else l = mid+1;
}
return nums[h];
}
34. Find First and Last Position of Element in Sorted Array
在排序数组中查找元素的第一个和最后一个位置
public int[] searchRange(int[] nums, int target) {
int[] res = {-1, -1};
if(nums == null || nums.length == 0) return res;
int l = 0, h = nums.length-1;
while(l < h) {
int mid = l+(h-l)/2;
if(nums[mid] >= target) h = mid;
else l = mid+1;
}
if(nums[h] == target) res[0] = h;
l = h;
h = nums.length-1;
while(l < h) {
int mid = l+(h-l)/2+1;
if(nums[mid] > target) h = mid-1;
else l = mid;
}
if(nums[l] == target) res[1] = l;
return res;
}