算法通关村第九关青铜挑战——透彻理解二分查找

大家好,我是怒码少年小码。

什么是二分查找

二分查找(二分搜索),是一种高效的查找算法。它的使用前提是要在有序数组中使用。通过将目标值与列表的中间元素进行比较,从而缩小查找范围,直到找到目标值或确定目标值不存在。

二分查找的基本步骤如下:

  1. 设定查找范围的开始索引 low 和结束索引 high,初始时 low = 0high = n-1,其中 n 是列表的大小。
  2. 计算中间索引 mid,即 mid = (low + high) / 2
  3. 将中间元素与目标值进行比较。如果中间元素等于目标值,则返回找到的元素/索引。
  4. 如果中间元素大于目标值,则将 high 更新为 mid - 1,即在左侧子列表中继续查找。
  5. 如果中间元素小于目标值,则将 low 更新为 mid + 1,即在右侧子列表中继续查找。
  6. 重复步骤 2 ~ 5,直到找到目标值或确定目标值不存在。

二分查找的时间复杂度为 O(log n),其中 n 是列表的大小。由于每次比较都将查找范围减半,因此它的查找效率非常高。

代码实现

循环方式实现

int binarySearch(int array[], int low, int high, int target) {
	while (low <= high) {
		int mid = (low + high) / 2;
		if (array[mid] == target) {
			return array[mid];
		}else if(array[mid] > target) {
			high = mid - 1;
		}else {
			low = mid + 1;
		}
	}
	return 0;
}

如果你能写出以上的代码,那你大致达到了70分,还有一些地方需要优化。

在计算机中除的效率很低,因此我们把除法换成移位。也就是

int mid = (low + high) / 2;
换成:
int mid = (low + high) >> 1;

至此你就有了80分,还有哪里需要改进呢?当low和high很大的时候,low + high 也很大,可能会溢出。我们可以改成

int mid = low + ((high - low) >> 1);

high - low表示high和low之间的距离,>>表示除与2,再加上 low 就可以得到中间位置的索引。至于为啥(high - low) >> 1之间要加括号,那是因为移位运算符的优先级小于加减号!!。最后改进版的如下:

int binarySearch01(int array[], int low, int high, int target) {
	while (low <= high) {
		int mid = low + ((high - low) >> 1);
		if (array[mid] == target) {
			return array[mid];
		}
		else if (array[mid] > target) {
			high = mid - 1;
		}
		else {
			low = mid + 1;
		}
	}
	return 0;
}

递归方式实现

如果能理解上面的方法,递归也就不难理解了。

int binarySearch02(int array[], int low, int high, int target) {
	while (low <= high) {
		int mid = low + ((high - low) >> 1);
		if (array[mid] == target) {
			return array[mid];
		}
		else if (array[mid] > target) {
			return binarySearch02(array, low, mid - 1, target);
		}
		else {
			return binarySearch02(array, mid + 1, high, target);
		}
	}
	return 0;
}

元素中有重复的二分查找

假如我们要找的元素在数组中有重复的,那么我们要返回的是最左边的那个。

显然这个问题的前提是我们已经找到了我们要找的元素,于是去检查它的左边有没有和他重复的元素,没有就返回它自己,有就一直往左走,一直到最左边的我们要找的元素

所以这个算法的其他分支和上面的相同,只有找到的if (array[mid] == target) 那条语句下方还要进行遍历和判断。

int binarySearch03(int array[], int low, int high, int target) {
	while (low <= high) {
		int mid = low + ((high - low) >> 1);
		if (array[mid] == target) {
			return array[mid];
		}
		else if (array[mid] > target) {
			high = mid - 1;
		}
		else {
			//找到元素之后,接着往左找看看有没有重复的
			while (mid != 0 && array[mid] == target) {
				 mid--;
			}
			if (mid == 0 && array[mid] == target) {
				return mid;
			}
			return mid + 1;
		}
	}
	return 0;
}

找到第一个不是目标元素的元素的时候while循环结束,此时的mid下标的元素是第一个不正确的元素,往后移动一个mid + 1的下标的元素才是我们要找的元素,所以最后才return mid + 1;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值