一文梳理二分查找算法【算法】【C语言】

在这里插入图片描述

0.前言

二分思想十分容易理解,但是二分法边界处理问题大多数人都是记忆模板,忘记模板后处理边界就一团乱。之前我也凭借记忆书写,没彻底理清二分的边界关系,这次就此机会理清。

记得某本书里有句经典发言:
90%的程序员不能完全正确的写一个二分查找算法。

1.故事分享📖

有一天小明到图书馆借了 N 本书,出图书馆的时候,警报响了,于是保安把小明拦下,要检查一下哪本书没有登记出借。小明正准备把每一本书在报警器下过一下,以找出引发警报的书,但是保安露出不屑的眼神:你连二分查找都不会吗?于是保安把书分成两堆,让第一堆过一下报警器,报警器响;于是再把这堆书分成两堆…… 最终,检测了 logN 次之后,保安成功的找到了那本引起警报的书,露出了得意和嘲讽的笑容。于是小明背着剩下的书走了。 从此,图书馆丢了 N - 1 本书。

保安怎么知道只有一本书📖没有登记出借,万一全部都没有登记呢?

这个故事其实说出了二分查找需要的条件:

  • 用于查找的内容逻辑上来说是需要有序的
  • 查找的数量只能是一个,而不是多个

二分查找

二分法最重要的两个点:

  • while循环中 left 和 right 的关系,到底是 left <= right 还是 left < right
  • 迭代过程中 middle 和 right 的关系,到底是 right = middle - 1 还是 right = middle

2.左闭右闭

image-20220305215431477

  • mid 是比较过的,因此 begin 是到 mid+1 的位置,end是到 mid-1 的位置

  • 循环条件要使用 while (begin <= end),因为当 begin = end这种情况发生的时候,得到的结果是有意义的

//左闭右闭的写法
int BinarySearch(int *a, int n, int x)
{
	assert(a);
	int begin = 0;
	int end = n-1;
	while (begin <= end)//这里用的是左闭右闭
	{
		int mid = begin + ((end - begin) >> 1);//避免相加可能溢出
		if (a[mid] < x)
			begin = mid + 1;
		else if (a[mid] > x)
			end = mid - 1;
		else
			return mid;
	}
	return -1;
}

3.左闭右开

image-20220305214523502

  • 当 x > mid 时,begin 要落到 mid+1 的位置,mid 的位置已经是查验过的了
  • 当 x < mid 时,end 要落到 mid 的位置,因为是左闭右开,实际上mid的位置是无法访问的
int BinarySearch(int *a, int n, int x)//对已经有序的数组进行搜索
{
	assert(a);
	int begin = 0;
	int end = n;
	while (begin < end)//这里用的是左闭右开 [begin,end)
	{
		int mid = begin + ((end - begin) >> 1);
		if (a[mid] < x)
			begin = mid + 1;//如果给的是 mid 有可能会死循环
		else if (a[mid] > x)
			end = mid;//如果到了mid-1 因为是开区间,其实找不到mid-1 的
		else
			return mid;
	}
	return -1;
}

4.错误示范

1.找不到

int BinarySearch(int *a, int n, int x)
{
	assert(a);

	int begin = 0;
	int end = n;
	while (begin < end)
	{
		int mid = begin + ((end - begin) >> 1);
		if (a[mid] < x)
			begin = mid + 1;
		else if (a[mid] > x)
			end = mid - 1;
		else
			return mid;
	}
	return -1;
}

int main()
{
	int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
	{
		printf("%d\n", BinarySearch(a, 10, i));
	}
	system("pause");
	return 0;
}
image-20220305220428229

2.死循环

如果 a[mid] < x 时 begin = mid 可能会死循环
begin指向6,end指向7,mid 还是指向6 ==》死循环

5.总结

二分法最重要的两个点,就是循环条件和后续的区间赋值问题
因为两者是相互联系,相互影响的,所以就需要两者统一,如果两者不统一,就会出现问题。所以循环条件和赋值问题必须统一。

在这里插入图片描述

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值