复杂的二分查找

1. 简介

    本篇文章主要讲稍微复杂一点的二分查找,这里的复杂是指在存在重复元素的有序数组中查找符合要求的值,主要探讨4种查找的情况。

  1. 二分查找,查找第一个等于给定值元素的索引
  2. 二分查找,查找最后一个等于给定值元素的索引
  3. 二分查找,查找第一个大于等于给定值元素的索引
  4. 二分查找,查找最后一个小于等于给定值元素的索引

2.内容

    针对上面的四种情况,下面具体阐述一下个人的见解。个人认为这4种查找主要是对于分界点的处理,1.当中间指针指向数组的最小值,即 mid=0;2.当中间指针指向数组的最大值,即 mid=n;3. 当a[mid-1] 或a[mid+1]和a[mid]的值不相同时候的处理。

2.1 查找第一个等于给定值元素的索引

2.1.1 代码

	// 二分查找,查找第一个等于给定值元素的索引
	// 对于这种情况,a[mid] < value 和 a[mid] > value 这两种情况和简单的二分查找相同
	// 主要就是 a[mid] == value 这种情况需要仔细处理一下
	public int binarySearch(int[] a, int value) {
		if(a == null){
			return -1;
		}
		int low = 0, hi = a.length - 1;
		while (low <= hi) {
			int mid = low + ((hi - low) >> 1);
			if (a[mid] < value) {
				low = mid + 1;
			} else if (a[mid] > value) {
				hi = mid - 1;
			} else {
				// a[mid] == value 的情况
				// 满足如下两种情况,则找到相应的值
				// 1. mid == 0,说明已经到数组最小值的边界,a[0]==value,返回mid(0) 即可。
				// 2. a[mid - 1] != value 而 a[mid] == value,说明 mid为分界点,返回mid即可。
				if((mid == 0) || (a[mid - 1] != value)) return mid;
				else hi = mid - 1;
			}
		}
		return -1;
	}

2.2 查找最后一个等于给定值元素的索引

2.2.1 代码

	//二分查找,查找最后一个等于给定值元素的索引
	// 对于这种情况,a[mid] < value 和 a[mid] > value 这两种情况和简单的二分查找相同
	// 主要就是 a[mid] == value 这种情况需要仔细处理一下	
	public int binarySearch(int[] a, int value) {
		if(a==null){
			return -1;
		}
		int n = a.length - 1;
		int low = 0, hi = n;
		while(low <= hi){
			int mid = low + ((hi - low) >> 1);
			if(a[mid] < value){
				low = mid + 1;
			}else if(a[mid] > value){
				hi = mid - 1;
			}else{
				// a[mid] == value 的情况
				// 满足如下两种情况,则找到相应的值
				// 1. mid == n,说明已经达到数组最大值的边界,a[n]==value,返回mid(n)即可。
				// 2. a[mid + 1] != value,而a[mid] == value,说明mid为分界点,返回mid即可。
				if((mid == n) || (a[mid + 1] != value)) return mid;
				else low = mid + 1;
			}
		}
		return -1;
	}

2.3 查找第一个大于等于给定值元素的索引

2.3.1 代码

	//二分查找,查找第一个大于等于给定值元素的索引
	//只需要分两种情况
	// 1. a[mid] >= value,说明数组中存在符合条件的值,需要找到分界点。
	// 2. a[mid] < value,此时不能确定数组中有无符合条件的值,继续缩小范围low = mid + 1;
	public int binarySearch(int[] a, int value) {
		if(a==null){
			return -1;
		}
		int n = a.length - 1;
		int low = 0;
		int high = n;
		while (low <= high) {
			int mid = low + ((high - low) >> 1);
			//a[mid] >= value成立
			if (a[mid] >= value) {
				// 满足如下两种情况,则找到相应的值
				// 1. mid == 0,说明已经到达到数组最小值的边界,a[0]>=value,返回mid(0)即可。
				// 2. a[mid - 1] < value,而a[mid]>=value,说明当前mid为分界点,返回mid即可
				if ((mid == 0) || (a[mid - 1] < value))return mid;
				else
					high = mid - 1;
			} else {
				low = mid + 1;
			}
		}
		return -1;
	}

2.4 查找最后一个小于等于给定值元素的索引

2.4.1 代码

	// 二分查找,查找最后一个小于等于给定值元素的索引
	// 只需要分两种情况
    // 1.a[mid] <= value,说明数组中存在符合条件的值,需要找到分界点。
	// 2.a[mid] > value,此时不能确定数组中有无符合条件的值,继续缩小范围high = mid - 1;
	public int binarySearch(int[] a, int value) {
		if(a==null){
			return -1;
		}
		int n = a.length - 1;
		int low = 0;
		int high = n;
		while (low <= high) {
			int mid = low + ((high - low) >> 1);
			//a[mid] <= value成立。
			if(a[mid] <= value){
				//满足如下两种情况,则找到相应的值
				//1. mid == n,说明已经达到数组最大值的边界,a[n]<=value,返回mid(n)即可
				//2. a[mid + 1] > value,而a[mid] <= value,说明mid为分界点,返回mid即可
				if((mid == n) || a[mid + 1] > value)return mid;
				else low = mid + 1;
			}else{
				high = mid - 1;
			}
		}
		return -1;
	}

3.声明

    这篇文章其实只能算半个原创,在极客时间王争讲解的《数据结构与算法之美》(付费文章无法给出链接)中他在讲解之前让我们试着自己实现一下,笔者也自己实现了相应的功能,但代码确实有些不够简洁,看了他的代码后确实简洁,于是写出了自己的一些见解。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值