来玩二分查找[NO.2]--左侧(右侧)边界的二分搜索

本文深入探讨了左侧边界二分查找的实现细节,通过对比普通二分查找,阐述了如何在数组中精确找到目标值的最左侧索引,特别适用于处理重复元素的情况。文章提供了具体的Java代码示例,解释了循环条件、边界调整等关键点。

什么是左侧(右侧)边界二分查找

上一篇博客,细诉了二分查找的细节和实现逻辑,当时存在局限性,比如有一个数组int[] arr={1,2,2,2,3,5,7},目标num=2,返回索引是3,假如需要返回最左侧索引(1)或最右侧的索引(3),这样的话普通二分查找是无法实现的

来一个左侧边界二分查找:

public static int getTwoNum5(int[] arr, int num) {
		int left = 0;
		int right = arr.length;
		// while(left < right) 终止的条件是 left == right,此时搜索区间 [left, left)
		// 为空,所以可以正确终止
		while (left < right) {
			int index = (left + right) / 2;
			System.out.println("当前索引位置:" + index + "索引值为:" + arr[index]);
			if (num == arr[index]) {
				// 当查询索引index查询到标的值,不立即返回而是缩小right边界[left,index]继续往左侧边界查找
				right = index;
			} else if (arr[index] > num) {
				// 初始二分索引值比标的num值大,查询范围[0,index]
				right = index;
			} else if (arr[index] < num) {
				// 初始二分索引值比标的num小,查询范围[index+1,right)
				left = index + 1;
			}
		}
		return arr[left] == num ? left : -1;
	}

细节问题:

  • 为什么right=arr.length而不是arr.length-1
    答:因为搜索区间是左闭右开的,当arr[index]=num或者arr[index]>num时,循环范围都是[left,index),这样不断搜索区间靠左查询。

  • 为什么while的循环条件是<而不是<=
    答:当查询最左侧或者最右侧边界成立时即arr[index]=num,右侧边界会赋值为right=index,如果循环结束条件leth<=right,最左侧情况(0,0)或最右侧情况(arr.lenght-1,arr.lenght-1)出现死循环无法改出。

  • 为什么right=index;left=index+1
    答:是为了切割二分查询后左右两侧边界,当初次二分索引查询值比标的num大时,查询范围切割为[left,index),当比标的num小时,查询范围切割为[index+1,right)。

  • 为什么numarr[index]时right=index
    答:当查询索引index查询到标的值num时,不立即返回而是缩小right边界[left,index]继续往左侧边界查找,可以一直查到最左侧,如果出现[index+1,right],当num
    arr[index],则查询直接返回

  • 如果是右侧边界的二分搜索
    答:只需要在num==arr[index]成立时,不立即返回而是缩小left边界,赋值left=index+1继续向右侧查找[index+1,right].

### LeetCode 二分查找完整代码实现 对于给定的有序数组 `nums` 和目标值 `target`,可以通过经典的二分查找算法来定位目标值的位置。以下是完整的 Java 实现: ```java public class Solution { public int search(int[] nums, int target) { int left = 0; int right = nums.length - 1; while (left <= right) { int mid = (right - left) / 2 + left; // 计算中间索引以防止溢出 if (target == nums[mid]) { // 找到目标返回其索引 return mid; } else if (target < nums[mid]) { // 目标小于中间值,在左半部分继续查找 right = mid - 1; } else if (target > nums[mid]) { // 目标大于中间值,在右半部分继续查找 left = mid + 1; } } return -1; // 如果未找到则返回-1表示不存在该元素 } } ``` 此段代码展示了如何通过调整左右边界逐步缩小搜索范围直到找到目标或者确认目标不在列表中的过程[^2]。 为了进一步扩展应用,考虑一些变体问题如第一个错误版本或是在已排序数组中寻找两个数之和等于特定值的情况也可以采用类似的逻辑框架并做适当修改以适应具体需求[^3]。 #### 寻找左侧边界二分查找 当题目要求找出某个条件成立的第一个位置时,则需要对上述标准形式稍作改动以便于处理这类情形: ```java int binarySearchLeftBound(int[] nums, int target){ int left = 0 , right = nums.length ; while(left<right){ int mid=(right-left)/2+left; if(nums[mid]==target) right=mid ;// 不立即返回而是收缩右侧边界尝试更早出现的目标 else if(nums[mid]<target) left=mid+1;// 继续向右探索更大的可能解空间 else if(nums[mid]>target) right=mid ;// 收缩右侧排除不可能区域 } if(left==nums.length || nums[left]!=target)// 检查越界以及最终结果验证 return -1 ; return left ; } ``` 这段代码实现了在存在重复项的情况下也能准确定位最左边匹配项的功能[^4]。 #### 应用于 Two Sum II 的解决方案 针对输入为升序排列数组求两数之和的问题,可以利用双指针加二分的思想快速得到答案: ```java public int[] twoSum(int[] numbers, int target) { for (int i = 0; i < numbers.length; ++i) { int low = i + 1, high = numbers.length - 1; while (low <= high) { int mid = (high - low) / 2 + low; if (numbers[mid] == target - numbers[i]) return new int[]{i + 1, mid + 1}; else if (numbers[mid] > target - numbers[i]) high = mid - 1; else low = mid + 1; } } throw new IllegalArgumentException("No solution"); } ``` 这里采用了固定一个数值作为基准再用另一个循环配合二分的方式高效解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值