二分查找方法详解

二分查找方法详解

一.二分查找框架

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

这个基本框架有以下几点需要注意:

  1. 不要使用else,而是使用else if 将所有情况都写清楚。
  2. 其中…标记的部分,就是可能出现细节问题的地方。多关注这些地方的变化。
  3. 计算mid时要防止溢出。代码中left+(right-left)/2 就和(left+right)/2的结果相同,但是有效防止了left和right太大直接相加导致溢出。

二.寻找一个数(基本的二分搜索)

即搜索一个数,如果存在,返回其索引,否则返回-1。

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 -1;
}

循环的终止条件是搜索区间为空的时候终止。

如果 right = nums.length;
那么while循环的判断条件应该为:while(left<right),这时候就需要在return的时候再做一个判断。

while(left<right){
	//...
}
return nums[left] == target?left:-1;

为什么是mid+1和mid-1?

根据刚才明确的【搜索区间】,本算法的搜索区间是[left,right],当我们发现索引mid不是要找的target,下一步应该去搜索哪里?肯定是搜索[left,mid-1]或者[mid+1,right],因为mid已经搜索过,应该从搜索区间中去除。

此算法框架的缺陷: 如果一个有序数组nums=[1,2,2,2,3],target为2,此算法返回的索引是2。但是如果想得到的是target的左侧边界,即索引1,或者想得到target的右侧边界,即索引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){
		int mid = left + (right-left)/2;
		if (nums[mid] == target){
			right = mid-1;
		}
		else if (nums[id]<target){
			left = mid+1;
		}
		else if (nums[id]>target)
			right = mid-1;
	}
	if(left>=nums.length || nums[left]!=target)
		return -1;
	return left;		
}

为什么该算法能够搜索左侧边界?
当找到target时,不返回对应的索引,而是缩小【搜索区间】的右边界right,不断向左收缩,这样最后跳出的时候就是target的左边界。

因为while的退出条件是left == right+1,所以有可能出现,left>nums.length的情况,所以需要对越界情况进行判断。

四.寻找右侧边界的二分查找

int right_bound(int []nums,int target){
	if(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)
			left = mid+1;
		else if (nums[mid] >target)
			right = mid-1;
		else if(nums[mid]<target)
			left = mid+1;
	}
	if(right<0 || nums[right]!=target)
		return -1;
	return right;
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值