二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。
要求:必须采用顺序存储结构,而且表中元素按关键字有序排列。
框架:
int binarySearch(int[] nums, int target) {
int left = 0, right = ...;
while(...) {
int mid = left + (right - left) / 2;//注意1
if (nums[mid] < target) {
left = ...
} else if (nums[mid] > target) {
right = ...
} else {
...
}
}
return ...;
}
注意1:mid不要写成mid = (right+left)/2,可能存在溢出。
基本的二分搜索(寻找一个数):
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 注意1
while(left <= right) { // 注意2
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1; // 注意3
else if (nums[mid] > target)
right = mid - 1; // 注意3
} else {
return mid;
}
return -1;
}
注意1:索引大小为 nums.length-1,相当于两端都闭区间 [left, right]。
注意2:while(left <= right)的终止条件是 left == right + 1。
注意3:去除已经搜索过的索引。
寻找左侧边界的二分搜索:
int left_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0;
int right = nums.length; // 注意1
while (left < right) { // 注意2
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid; // 注意3
} else {
right = mid;
}
}
return left;
}
注意1:索引大小为 nums.length,相当于两端都闭区间 [left, right)。
注意2:while(left < right)的终止条件是 left == right。
注意3:始终保持搜索区间为左闭右开。
寻找右侧边界的二分查找:
int right_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0, right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid;
} else {
left = mid + 1; // 注意1
}
}
return left - 1; // 注意2
注意1:nums[mid] == target 时,不立即返回,而是增大「搜索区间」的下界 left,使得区间不断向右收缩,达到锁定右侧边界的目的。
注意2:while 循环结束时,nums[left] 一定不等于 target 了,如果存在target,nums[left-1]必定等于target。
差值查找:
int mid = left + (right - left) * (target- num[left]) / (num[right] - num[left]);
对于数据量较大,关键字分布比较均匀的查找表来说,采用插值查找, 速度较快。