二分查找法

具体讲解练习:在一个有序数组中查找具体的某个数字n 

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	//找7  在一个有序数组中查找具体的某个数字n
	int k = 7;
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);//用整个数组的大小除以第一个元素大小就是元素个数
	for (i = 0; i < sz; i++)
	{
		if (arr[i] == k)
		{
			printf("找到了,下标是:%d", i);
			break;
		}
	}
	if (i == sz)
	{
		printf("找不到\n");
	}
	return 0;
}

这个代码没有很好的利用这个数组是有序的,上面这个写法,无序的数组也可以这样写,一个一个遍历,这种效果其实是比较慢的。

假设我买了一双鞋,这双鞋的价格呢,你不知道,你问我呢,你说,这双鞋的价格是多少呢,我说,不超过300,你会怎么猜,你肯定不会猜1块、2块、3块......,这里呢不要这样去猜,不建议,那这样你可能会猜150,我告诉你,小了,那这个时候就在150到300之间猜一个225,这个时候就会发现,你猜150的时候告诉你小了,那这个时候1到150就不用再去猜了,就去掉了一半,这样猜的效率就比较高了,而如果这个时候告诉你225也小了,那这个时候就在226到300之间再去猜一个数字,这样猜的速度就块很多。我就猜,每次猜你这个数的中间这个值。

此时让你去找1 2 3 4 5 6 7 8 9 10 里面的那个值,你会怎么找。口头说很简单,但写成代码要怎么做呢。

我先拿出中间的元素跟我要找的比一下,我看是在左边还是在右边,就可以直接去掉一半。

那首先找到这个数字里面的中间元素,怎么找呢,

我先找一个左边的下标,左边元素的下标我们叫left,这个要注意,我们知道这个下标是0

那我再找这个数组的右边元素的下标,叫right,这个下标是9

中间元素的下标是什么呢,那我们就说left和right的平均值,9和0的平均值,是4,数组元素是5

 然后发现这个5比我们要找的这个k(k=7)要小,说明我要找的在5的右边,本来被查找的范围其实是1 2 3 4 5 6 7 8 9 10 ,而当我确定在我的右边时,我被查找的范围就变成了 6 7 8 9 10,这个时候我查了一次,数据就少了一半,我被查找的范围就变成了 6 7 8 9 10 的时候,被查找范围的左下标就变了,右下标没变此时left应该指到6这里,前面这个中间5的下标是4,如果我们叫mid的话,那这个时候的left应该被改成 mid+1 ,(4+1就是5嘛),5作为下标找到了我们的元素6,这个时候新的范围就是left和right锁定的范围,范围就少了一半

 所以这种查找算法我们就叫折半查找,或者叫二分查找

当我发现这个地方新的范围的下标变成了5到9,左下标是5,右下标是9的时候,在元素6到10里面去找那个7,怎么找呢,再把左右下标的平均值求出来,是7,7作为下标找到是这个元素8,,然后再拿这个元素8和我要找的元素7比一下,比我找的元素7要大,说明我要找的元素在元素8的左边,这个时候被查找的范围一下就缩减到了元素6到7,

这个元素6到7的左下标还是left,但是右下标就变成了元素7的下标,也就是下标6,注意,刚刚这个6 7 8 9 10里面的这个中间元素下标mid,这个 mid-1 其实就是我们这个新的范围的right的下标,

所以就会发现在这个过程中,我们总是通过左右下标,确定一个中间元素下标,中间元素如果不是我要找的那个元素,那这个地方我们再来确定我们新的查找范围,新的查找范围的左右下标,

这个时候我们确定了左下标是5,右下标是6,5和6的平均值是5,5锁定一个元素的时候是元素6,

比我找的元素7要小,说明我要找的元素在元素6的右边,这个时候我们查找的范围就是就是元素7了,7的左下标是6,右下标还是6,6和6的平均值是6,6作为下标锁定的元素是7,和我们要找的7一样,

我找了,如果这个元素7都不是我要找的元素,那这个元素就再也找不到了,我已经把我的范围缩小到不能再缩小了。

注意,我划过的圈,就是我找过的元素,现在十个数里边,如果我要遍历,从前往后去遍历,我要找一个数字的话,十个数字最坏的情况下,我要找10次,而如果是刚刚这个情况,我折半查找算法,会发现,元素7已经是这个时候已经是刚刚最坏的情况了,如果7不是我要找的元素,就再也找不到了,会发现我找了4次,会发现效率明显提升了很多。前提条件是这个数据必须是有序的

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	//找7  在一个有序数组中查找具体的某个数字n
	int k = 7;
	int sz = sizeof(arr) / sizeof(arr[0]);//用整个数组的大小除以第一个元素大小就是元素个数
	//sz保存的是数组元素个数
	int left = 0;//左下标
	int right = sz-1;//右下标 ,如果这个数组是10个元素的话,right下标就是10-1=9,就是9,
	//也就是说这个跟元素个数有关系
	//当我这个左右下标都确定的时候,要找出中间元素的下标
	while (left <= right)  //当left小于等于right时,说明中间还有元素,还有待元素被查找
	{
		int mid = (left + right) / 2;//中间元素下标
		if (arr[mid] < k)
		{
			left = mid + 1;
		}
		else if (arr[mid] > k)
		{
			right = mid - 1;
		}
		else
		{
			printf("找到了,下标是:%d\n",mid);
			break;
		}
	}
	if (left > right)
	{
		printf("找不到\n");
	}
	return 0;
}

这个代码虽然比前面写的代码要麻烦些,但这个效率要比前面的要高

查找一次,数据少一半,查找一次,数据少一半......,会发现,这个数据越多,这个效率其实越高

比如说这里有42亿个数据,查到一半,就把21亿多的数据给干掉了,查一次少一半,查一次少一半,这个效率非常快的,

这里的时间复杂度是\log_{2}n,如果n=2^{32},  把这个n代到\log_{2}n=32,也就是说,你有2^{32}个有序的数据放在我的面前,我也只查32次就能搞定,(2^{32}是42个多亿个数字)

当然,这个折半查找不一定是最快的,他的条件是比较苛刻的,是要有序的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值