二分查找

二分查找的大前提:数组有序


一、无重复元素的二分查找

左闭右闭的二分查找算法(推荐

public void binarySearch(int[] nums,int target){
	int left = 0;
	int right = a.length - 1;
	while(left <= right){
		int mid = (left + right) >>> 1;
		if(target < nums[mid]){
			right = mid - 1;
		}else if(nums[mid] < target){
			left = mid + 1;
		}else{
			return mid;
		}
	}
	retun -1;
}

问题?

  1. 为什么是left<=right,而不是left<right
      当 target 是数据中的第一个元素的时候,使用 < 会将这个元素跳过去,导致没有查找到目标元素。
  2. 为什么 mid 计算的时候,不使用(left+right)/2
      在Java中 int 类型是有范围限制的,如果超过这个范围,他就会将二进制的最高位当作是符号位(1是负数,0是正数),那么他最终得到的中间索引 mid 就可能是一个负数。导致最终出错。
     例子:
    在这里插入图片描述

结果:
在这里插入图片描述
所以这里推荐(left + right) >>> 1或者left + (right - left)/ 2


左闭右开的二分算法

public void binarySearch(int[] nums,int target){
	int left = 0;
	int right = a.length;
	while(left < right){
		int mid = (left + right) >>> 1;
		if(target < nums[mid]){
			right = mid;
		}else if(nums[mid] < target){
			left = mid + 1;
		}else{
			return mid;
		}
	}
	retun -1;
}

  相较于第一种,这种二分查找算法的右边界是不参比较的。因此,这里的right=mid。也就是说如果使用[left,mid-1),他就会导致mid-1为目标元素的时候,会导致错过这个元素,找不到目标元素。
如何理解这个,错过mid-1这个元素呢?
  当right = mid - 1;的时候,如下:
在这里插入图片描述
也就说:左闭右开的这中二分查找算法,右边界的这个元素是不参与比较的,如果我们的右边界移动范围为right = mid - 1,这就会导致mid-1处的这个值错过比较,而我们使用right = mid;就不会有这种情况。

平衡版的二分查找

  平衡版是无论最好最坏的情况,它的时间复杂度都为 O(log(n))。因为他总是在循环完之后才进行结果的比较。
可以这样理解:按照前面的二分查找算法

  1. 当目标元素刚好在第一次 mid 处时,这就是他最好的时间复杂度 O(1)。
  2. 当目标元素小于 nums[mid] 的时候,他就直接判断target < nums[mid](因为我们这个条件在第一个),这次计算次数我们记为L。
  3. 当目标元素大于 nums[mid] 的时候,那么就他得先去判断target < nums[mid],由2知这个判断次数为L。之后才去执行我们的nums[mid] < target,那么他总共就得执行2L。

对于这种因为目标元素在 mid 的左边还是右边,影响我们的执行次数的情况就是不平衡

public int binarySearch(int[] nums,int target){
   int left = 0;
   int right = nums.length;
   while(1 < (right-left)){
       int mid = (left + right) >>> 1;
       if(target < nums[mid]){
           right = mid;
       }else{
           left = mid;
       }
   }
   return nums[left] == target ? left : -1;
}

 这里我个人推荐使用第一种左闭右闭的二分查找算法,他理解起来简答,而且它循环里边的判断有规律,不必去考虑边界元素的问题。
 还有一点就是,我下边的二分查找算法都是在它基础上改的。(当然也可以在其他的二分查找算法基础上改,这里只是说我写的这个。)

二、有重复元素的二分查找

返回最左边的索引位置的二分查找

public int binarySearch(int[] nums,int target){
	int left = 0;
	int right = nums.length - 1;
	int condition = -1;
	while(left <= right){
		int mid = (left + right) >>> 1;
		if(nums[mid] < target){
			left = mid + 1;
		}else if(target < nums[mid]){
			right = mid - 1;
		}else{
			condition = mid;
		// 如果找到了目标元素,继续向左查找
			right = mid - 1;
		}
	}
	return condition;
}

返回最右边的索引位置的二分查找

public int binarySearch(int[] nums,int target){
	int left = 0;
	int right = nums.length - 1;
	int condition = -1;
	while(left <= right){
		int mid = (left + right) >>> 1;
		if(nums[mid] < target){
			left = mid + 1;
		}else if(target < nums[mid]){
			right = mid - 1;
		}else{
			condition = mid;
		// 如果找到了目标元素,继续向右查找
			left= mid + 1;
		}
	}
	return condition;
}

  二分查找重复元素,返回元素的最左边还是最右边的元素,只是取决于他找到目标元素之后,他的边界条件的变动。如果是变动左边界就是返回最右边的索引,反之则是最左边的元素。

 含重复元素的,思路都是一样的。所以这里根据自己的需求来就行。

三、返回值有效的二分查找

  何为返回值有效的二分查找?在前边的二分查找算法中,都是如果目标元素在数组中那么就返回元素在数组中的索引位置,反之就返回-1。这个-1就是一个无效的值。
  何为返回值有效?主要针对目标元素不在数组中,返回值是-1的情况。我们这里返回的是不存在的这个元素应该在数组中那个位置插入

二分查找(返回值>=target的最左索引)(推荐

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

  这种返回值是:

  1. 如果目标元素存在,返回的是最左的索引(包含数组中有重复元素的情况)。
  2. 如果目标元素不存在,返回的是它插入的位置。

二分查找(返回值<=target的最右索引)

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

  这种返回值是:

  1. 如果目标元素存在,返回的是最右的索引(包含数组中有重复元素的情况)。
  2. 如果目标元素不存在,返回的是距离它左边最近的那个元素的位置。

 我个人这里推荐使用第一种这个有效返回值的二分查找算法,他理解起来也很容易。

综上:我们在需要返回值有效的情况下,记住二分查找(返回值>=target的最左索引)这个就可以了。如果找不到返回值就得是-1,那么记住中的就行。
当然:我们也可以将二分查找(返回值>=target的最左索引)的返回条件改成:

return nums[left] == target ? left : -1;

这既满足含重复元素数组的情况,又在目标元素不存在的时候,返回-1。

关键是理解这其中的各种情况下的边界条件。在理解之后,可以按照自己的需求进行修改。


如果文章中有描述不准确或者错误的地方,还望指正。您可以留言📫或者私信我。🙏
最后希望大家多多 关注+点赞+收藏^_^,你们的鼓励是我不断前进的动力!!!
感谢感谢~~~🙏🙏🙏

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺术留白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值