特点:被搜索序列有序;可以随机访问
二分查找一般由三个主要部分组成:
- 预处理 —— 如果集合未排序,则进行排序。
- 二分查找 —— 使用循环或递归在每次比较后将查找空间划分为两半。
- 后处理 —— 在剩余空间中确定可行的候选者。
1. 时间复杂度
二分搜索的时间复杂度正比于while的循环次数
循环次数 | 剩余元素 |
---|---|
1 | N/2 |
2 | N/2^2 |
3 | N/2^3 |
k | N/(2^k) |
易知,剩余元素 >=1,
最差情况是在剩余一个元素的情况下找到目标值,则有 N/(2^k) = 1
得到循环次数为k = log(2^N)
,
时间复杂度可表示为O(N) = O(logN)
2. 模版
Template #1:
int binarySearch(int[] nums, int target){
if(nums == null || nums.length == 0)
return -1;
int left = 0, right = nums.length - 1;
while(left <= right){
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if(nums[mid] == target){ return mid; }
else if(nums[mid] < target) { left = mid + 1; }
else { right = mid - 1; }
}
// End Condition: left > right
return -1;
}
Exercise #1:求一个数的平方根
返回类型为 int ,开方后的小数部分略去
public int sqrt(int x) {
if (x == 0)
return 0;
int left = 1, right = x;
while (true) {
int mid = left + (right - left)/2;
if (mid > x/mid) { //不能换为 mid * mid > x ,因为当 mid 过于大时,平方后会溢出
right = mid - 1;
} else {
if (mid + 1 > x/(mid + 1))
return mid;
left = mid + 1;
}
}
}
Exercise #2:猜数字大小
You call a pre-defined API guess(int num)
which returns 3 possible results (-1
, 1
, or 0
):
-1 : My number is lower
1 : My number is higher
0 : Congrats! You got it!
public class Solution extends GuessGame {
//My" means the number which is given for you to guess not the number you put into guess(int num).
public int guessNumber(int n) {
if(guess(n) == 0) return n;
int left = 1, right = n;
while(left <= right){
int mid = left + (right - left)/2;
if(guess(mid) == 0) return mid;
else if(guess(mid) == -1) right = mid - 1;
else left = mid + 1;
}
return -1;
}
}
Exercise #3:搜索旋转排序数组
0 1 2
6 7 0
7 0 1
- 因为 Binary Search 需要在获得中间数后,判断下一次是搜索左半段还是右半段,可以用
mid
值和right
值比较:
若mid 值 < right 值
说明右半段;否则,左半段有序; - 在有序的半段中判断目标值是否在此区域内,以确定要保留哪边。
public int search(int[] nums, int target) {
if(nums == null || nums.length == 0) return -1;
int left = 0, right = nums.length - 1;
while(left <= right){
int mid = left + (right - left) / 2;
if(nums[mid] == target) return mid;
else if(nums[mid] < nums[right]){ //右半段有序
if(nums[mid] < target && nums[right] >= target) left = mid + 1;
else right = mid - 1;
}else{
if(nums[mid] > target && nums[left] <= target) right = mid - 1;
else left = mid + 1;
}
}
return -1;
}
Template #2:
左闭右开区间:right = nums.length
, right = mid
int binarySearch(int[] nums, int target){
if(nums == null || nums.length == 0)
return -1;
int left = 0, right = nums.length;
while(left < right){
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if(nums[mid] == target){ return mid; }
else if(nums[mid] < target) { left = mid + 1; }
else { right = mid; }
}
// Post-processing:
// End Condition: left == right
if(left != nums.length && nums[left] == target) return left;
return -1;
}
网友评论