前言
对于二分查找,也可以叫折半查找,你一定不会陌生,以前应该也有玩过猜价格的有戏,让你在某个价格区间内,猜某个商品的价格,如果没猜到就告诉你是猜多了还是猜少了,然后让你继续猜,这其实就可以运用到二分查找的方式来最快的猜到那个价格。
高效的查找方式
二分查找是一种非常高效的查找方式,对于一个2的32次方的数来说,大约有42亿之多,但由于二分查找拥有logn的时间复杂度,所以也就最多只需要32次就能查找出结果。
简单但不容易
二分查找虽然思路非常简单,但是要想写出正确的二分查找,其实并没有那么容易,这点我相信只要写过的都应该能够体会到,主要问题就在于边界的处理。
代码实现
public int binarySearch(int[] nums, int target) {
int l = 0;
int r = nums.length - 1;
while (l <= r) {
//找到中点,这样写l + ((r - l) >> 1) 等同于 (l+r)/2,但是可以防止l+r溢出
int mid = l + ((r - l) >> 1);
//如果等于目标值直接返回
if (nums[mid] == target) {
return mid;
}
//如果中间点大于目标值,则找左半边,否则找右半边
if (nums[mid] > target) {
r = mid - 1;
} else {
l = mid + 1;
}
}
//找不到返回-1
return -1;
}
容易出问题的地方
-
问题1:找中点,l + ((r - l) >> 1)对于奇数来说,找的刚好就是中间的位置,对于偶数来说,找的是靠左边的位置。
这样写可以防止(l+r)/2时,l+r可能会溢出的问题。 -
问题2:while (l <= r),注意这里是 l<=r,而不是l<r
如果写成l<r,就会把第一个和最后一个位置的元素漏掉。 -
问题3:r和l的更新,应该是mid-1或者是mid+1
如果直接更新为mid,则可能会发生死循环,比如把r = mid-1,直接写成r = mid,然后在1,2,3,5,6,7,8,查找4,就会发生死循环。因为最后会算出l==r(都等于3),mid等于3,并且nums[mid](5)) > target(4),然后r被无限赋值为3。
限制条件
虽然二分查找非常的快,但也是有一定的局限性的,其中最大的问题就是要求待查找的数据是有序的,其次要求支持高效的随机访问,如果每一次定位中间位置都需要花费额外的时间去查找,那就不能说其整个时间复杂度是logn的了,最后既然要求有序还要支持随机访问,那能想到的数据结构就是数组了,所以一般对于二分查找来说,还需要连续的内存空间,如果二分查找的数据量非常的大,可能对内存的要求也比较苛刻。