二分查找及其代码优化(C语言实现)

二分查找

定义

二分查找,又称折半查找,如果对当前区间查找成功,则直接返回该元素及其数组下标;如果对当前区间查找失败,则在当前空间的中间元素与待查元素进行比较后,将查找区间转变为可能包含该元素的数组区间继续查找,该区间为原区间长度的一半,直至查找成功或者查找失败为止。

时间复杂度

二分查找的时间复杂度为 O(log n)

所需数组类型

所需数组类型为有序数组

算法思想

假设原数组为{4,6,7,8,10,23,24,36},数组为整型数组。我们假定 left 和 right 都是指向区间边界的变量,其值为数组下标的值,其中 left 指向所查找区间的最左边,right 指向所查找区间的最右边。

我们假设对 36 进行查找:

  1. 首先,我们置:left=0,right=7(数组长度-1),由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  2. 此时,我们对a[mid]值和所查找值进行比较,因为 8<36,并且由于所查数组为增序数组,所以要查找的值应在 [mid,right]之间。此时,我们置 left=mid+1,由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  3. 此时,我们对a[mid]值和所查找值进行比较,因为 23<36,并且由于所查数组为增序数组,所以要查找的值应在 [mid,right]之间。此时,我们置 left=mid+1,由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  4. 此时,我们对a[mid]值和所查找值进行比较,因为 24<36,并且由于所查数组为增序数组,所以要查找的值应在 [mid,right]之间。此时,我们置 left=mid+1,由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  5. 此时,我们对a[mid]值和所查找值进行比较,因为 36=36,所以查找成功,返回查找成功值(a[mid])和所查找值在数组中的位置(mid)。

我们假设对 4 进行查找:

  1. 首先,我们置:left=0,right=7(数组长度-1),由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  2. 此时,我们对a[mid]值和所查找值进行比较,因为 8>4,并且由于所查数组为增序数组,所以要查找的值应在 [left,mid]之间。此时,我们置 right=mid-1,由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  3. 此时,我们对a[mid]值和所查找值进行比较,因为 6>4,并且由于所查数组为增序数组,所以要查找的值应在 [left,mid]之间。此时,我们置 right=mid-1,由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  4. 此时,我们对a[mid]值和所查找值进行比较,因为 4=4,所以查找成功,返回查找成功值(a[mid])和所查找值在数组中的位置(mid)。

我们假设对 23 进行查找:

  1. 首先,我们置:left=0,right=7(数组长度-1),由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述
  2. 此时,我们对a[mid]值和所查找值进行比较,因为 8<23,并且由于所查数组为增序数组,所以要查找的值应在 [mid,right]之间。此时,我们置 left=mid+1,由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述
  3. 此时,我们对a[mid]值和所查找值进行比较,因为 23=23,所以查找成功,返回查找成功值(a[mid])和所查找值在数组中的位置(mid)。

我们假设对 1 进行查找:

  1. 首先,我们置:left=0,right=7(数组长度-1),由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  2. 此时,我们对a[mid]值和所查找值进行比较,因为 8>1,并且由于所查数组为增序数组,所以要查找的值应在 [left,mid]之间。此时,我们置 right=mid-1,由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  3. 此时,我们对a[mid]值和所查找值进行比较,因为 6>1,并且由于所查数组为增序数组,所以要查找的值应在 [left,mid]之间。此时,我们置 right=mid-1,由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  4. 此时,我们对a[mid]值和所查找值进行比较,因为 4>1,并且由于所查数组为增序数组,所以要查找的值应在 [left,mid]之间。此时,我们置 right=mid-1,由于 mid 为所查区间的中间值,故 mid=(left+right)/2(mid值为向下取整)。
    在这里插入图片描述

  5. 此时,我们对a[mid]值和所查找值进行比较,因为 right<left,所以查找失败,返回-1,提示所查数不在当前数组中。

代码实现

#include<stdio.h>
//接收数组,数组长度,所查值
int Binarylookup(int a[], int length, int targe)
{
	//定义left,right,mid
	//left指向所查区间的最左边
	//right指向所查区间的最右边
	int left = 0, right = length - 1, mid;
	//当right<left时,说明查找失败,结束查找
	while (right >= left) {
	    //mid指向区间中间值
		mid = (left + right) / 2;
		//查找成功
		if (a[mid] == targe) {
			printf("%d在数组中存在,数组下标为%d\n", targe, mid);
			return 0;
		}
		//所查值在mid的右边
		else if (a[mid] < targe)
			left = mid + 1;
		//所查值在mid的左边
		else
			right = mid - 1;
	}
	printf("%d在数组中不存在\n", targe);
}
int main()
{
	int a[] = { 4,6,7,8,10,23,24,36 };
	//调用二分查找函数
	Binarylookup(a, 9, 36);
}

优化区间数据溢出

数据溢出原因

我们知道 int 型数据在32位和64位机器中都是占4个字节,一个字节占8位,故 int 型应该占 32 位。在这32位中,最后一位(32位是符号位,0表示正数,1表示负数,它不表示数据值,而剩下的31位(1~31位存储数据
在这里插入图片描述
接下来,我们看个数据计算:
我们知道:2147483647 + 1073741823 = 3221225470
但如果把这两个当做 int 型来进行计算的话,则是这样:2147483647 + 1073741823 = -1073741822
毫无疑问,说明计算结果超出了int型范围,发生了溢出,此时计算结果已经变得不可靠了。此时我们看下二分查找的代码:

mid = (left + right) / 2;

因为 left 和 right 两者都是 int 型,倘若输入的两者数据也是接近于2的31次方,那我们计算出来的 mid 结果也是溢出结果,那计算就无法进行下去。

解决溢出

我们知道 left <= right ,所以我们只需把加法变成一个减法运算,使结果不溢出即可。即:

mid = (left + right) / 2
    = left / 2 + right / 2
    = (left - left / 2) + right / 2
    = left - left / 2 + right / 2
    = left + (right -left) / 2

由于 >>2 相等于除二,并且右移在计算机中的运行效率要高,所以此处的除二,还可以换成 >>2

mid = = left + (right -left) >> 2

代码实现(优化运算溢出)

#include<stdio.h>
//接收数组,数组长度,所查值
int Binarylookup(int a[], int length, int targe)
{
	//定义left,right,mid
	//left指向所查区间的最左边
	//right指向所查区间的最右边
	int left = 0, right = length - 1, mid;
	//当right<left时,说明查找失败,结束查找
	while (right >= left) {
	    //mid指向区间中间值
		mid = left + (right - left) >> 2;
		//查找成功
		if (a[mid] == targe) {
			printf("%d在数组中存在,数组下标为%d\n", targe, mid);
			return 0;
		}
		//所查值在mid的右边
		else if (a[mid] < targe)
			left = mid + 1;
		//所查值在mid的左边
		else
			right = mid - 1;
	}
	printf("%d在数组中不存在\n", targe);
}
int main()
{
	int a[] = { 4,6,7,8,10,23,24,36 };
	//调用二分查找函数
	Binarylookup(a, 9, 36);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值