二分查找小结


1.二分查找通用模板

二分查找的前提条件:保证元素有序

public int binarySerach(int[] nums, int target) {
	// 定义初始边界
	int left = 0;
	int right = ... ;
	
	// 开始查找
	while(...) {
		// 找寻中点
		int mid = left + (right - left) / 2;  //防止计算溢出
		// 判断中点和目标的大小
		if (nums[mid] == target) {
			...
		}
		else if (nums[mid] < target) {
			...
		}
		else if (nums[mid] > target) {
			...
		}
	}
	// 循环结束,没找到目标元素,返回相应结果
	return ... ;
} 

上面中的模板留白部分,其实有很多细节要深入讨论,不然极有可能导致二分查找失败

2.1 搜索区间

搜索区间指的是 leftright 的下标,通常有两种情况:
(1)左右皆闭 [left,right]
(2)左闭右开 [left, right)
不同的搜索区间的应用场景多不相同。第一种通常适合查找数组中的指定单个元素,第二种在搜索左右边界时常用。


2.2.1 左右皆闭

public int binarySearch(int[] nums, int target) {
    int left = 0; 
    int right = nums.length - 1; 

    while(left <= right) {
        int mid = left + (right - left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; 
        else if (nums[mid] > target)
            right = mid - 1; 
    }
    return ... ;
}

这种情况下,right 通常赋值为 nums.length-1right 没有越界,这时我们搜索的是整个 [left, right] 区间:
(1) while循环的判断条件
因此 while 循环的条件常用 <=,这样做在最后的判断之前,leftright指向同一个位置mid,条件判断处理后的区间变为了 [right+1, right],循环终止,正常退出。
当循环条件用 < 时,最后的判断之前,leftright紧挨着,条件判断处理后区间变为了 [right, right],这时候区间非空,循环终止,但仍旧有一个元素没有被搜索,直接返回是错误的。

(2) left和right下标的移动
在本例子左右皆闭中,整个区间是 [left, right], 如果 mid 不是我们要找的target,下一步应去 [left, mid-1] 或者 [mid+1, right],因为 mid 已经被搜索过了


2.2.2 左闭右开

上述全闭空间中找单个元素非常有利,但是一旦找寻左右边界就缺陷率。如给定数组[0,1,2,3,3,3,4],target=3,找寻target的左右边界就有问题了

以下为寻找左侧边界的示例

public int binaryLeftBound(int[] nums, int target) {
	int left = 0, right = nums.length; // right指向越界下标,左闭右开区间
	
	while(left < right) { // 最后判断之前区间为[left, right), 已经只有一位元素了nums[left]了
		int mid = left + (right - left) / 2;
		if(nums[mid] == target) {
			right = mid; // 核心部分,缩小上界区间,在[left, mid)中继续搜索
		}
		else if(nums[mid] < target) {
			left= mid+1; // 左闭右开原则
		}
		else if(nums[mid] > target) {
			right = mid;	// 左闭右开原则,实际上里面的元素区间到mid-1
		}
	}
	// 防止越界
	if(left >= nums.length || nums[left] != target) {
		return -1;
	}
	return left;
} 

Q: 为什么能够搜索左边界?
A: 上述代码的一个核心在于 mid 找到目标后,不返回,而是缩小上界区间,在 [left, mid) 中继续搜索,不断左缩

Q:没有找到目标时,如何防止索引越界?
A:在该种情况下,使用左闭右开搜索区间,退出循环时,left=right,如当 target 比 nums 中所有的元素都大时,就会导致 left=right=nums.length,因此要做判断。

搜寻右边界时,模板如下:

public int binaryRightBound(int[] nums, int target) {
	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) {
			left = mid+1; //左闭右开原则
		}
		else if(nums[mid] > target) {
			right = mid; //左闭右开原则
		}
	} 
	// 越界判断,退出循环时,left=right
	if(right <= 0 || nums[right-1] != target) {
		return -1;
	}
	return right-1; 
}

Q:为什么返回 right-1,而不是right?
A:当结束while循环时,nums[left] 不一定是target。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值