希望每一个想开始而又不敢开始的人,对任何知识都不要抱有恐惧,觉得它很神秘,那样不好,其实知识就在那里一动不动,一层薄纱,去掀开它,你将会看见新的世界。
🌈前言
二分法查找算法,也叫折半查找算法,将一个有序数组(采用顺序存储结构有序排列)通过中间值和目标元素不断的折半对比缩减区间,直到在区域内找到目标元素为止。
我们先来了解一下传统的查找方法,来对比出为什么要使用二分法
1. 传统循环遍历寻找目标元素
- 此处有一个数组,共计8个元素
- 按照一般循环遍历的方法,我们要从第一个元素开始一一遍历直到寻找到目标元素,假如目标元素是8,该方法就要循环8次;
代码示例:
#include <stdio.h>
#include <string.h>
int main()
{
//创建一个数组
int arr[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
//创建一个变量用于记录数组下标的变化
int i = 0;
//创建一个变量用于接收用户需要寻找的元素
int n = 0;
scanf("%d\n", &n);
for (i = 0; i < 8; i++)
{
if (arr[i] == n)
{
printf("该元素位于下标 %d 位置", n);
break;
}
}
return 0;
}
当然如果数组的大小较小,且目标元素刚好位于有序数组的最前方,传统方法比较简单易懂;但是如果数组的大小极大,此时就要循环很多次,代码耗时较长;
我们再来看看二分法查找
2 二分查找
2.1 运行机理
大致思路:
二分查找先要确定一个左下标和一个右下标,求出中间元素的下标,再根据中间元素的下标,来判断和数字的关系,来进行左右下标的调整,判断后续的查找范围
我们先假设一个有序数组,如下图:
假设目标元素为7
代码运行过程详解:
- 先确定左、右下标,分别为0和7,然后相加除以2求出中间元素的下标为3(整型的除法结果不会四舍五入只取整数部分);
- 再根据中间下标3对应的元素4与目标元素的大小关系为4<7,因此我们的查找范围缩小至下标4到下标7之间;
- 此时左下标变为4(原中间元素下标+1)而非0,右下标为7不变,得到中间元素的下标为5,对应元素6<7;
- 再次缩小范围,左下标变为6(原中间元素下标+1),右下标依旧不变,得到中间元素的下标为6,对应元素为7=7,找到目标元素
代码示例:
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int left = 0;
int sz = sizeof(arr) / sizeof(arr[0]);//求出数组元素个数
int right = sz - 1;
int mid = left + (right - left) / 2;
int k = 7; //目标元素
int flag = 0;//用于最后判断是否找到
while (left <= right)
{
mid = left + (right - left) / 2;//实时更新中间元素的值
if (arr[mid] < k)
{
left = mid + 1;//更新查找范围
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
flag = 1;//前后呼应,当找到的时候,代表flag被赋值为1,
//如果没找到说明程序不会运行进来,flag不被赋值;
printf("找到了,目标元素在下标为:%d 的位置", mid);
break;
}
}
if (0 == flag)
{
printf("没找到");
}
return 0;
}
2.2 模糊点
相信大家在了解二分法时都会有一些不清楚的地方,如,数组的大小及数组的下标的奇偶性对二分法会不会有什么计算错误或代码更改、为什么mid的计算要拐一道弯
2.2.1 数组大小及下标奇偶性
当数组大小为偶数时,右下标即为奇数,反之亦然,二分法在进行二分计算时,因为是编程的整型计算,只取整数部分,因此,就好比控制变量法,除法规则不变后,当数组大小偶数个数实际上中间数的个数应该是两个才算对称,但是进二分计算后,一定会得到一个中间数,会是两个中的前一个,但是将中间数和目标元素一对比就立马重新划分区域;数组大小为奇数,是有一个可以除的尽的中间数,然后将中间数和目标元素进行对比即可重新划分区域;所以二分法大家不要因为二分觉得奇偶不一样,除以2是否除尽就会有不同,其实都只是对比后划分一个区域而已,并不会影响任何结果。
2.2.2 mid的计算
大家可能奇怪为什么mid不就是按照左下标加上右下标之后除以2吗,为什么要用更复杂的计算公式呢?
实际上是当二分法重复进行多次后,假如数组大小极大,数组最大下标并没有溢出,但是当左下标加右下标数值过大超过了整型所能表达的最大值,导致了溢出,数值也就会出现错误,那个时候中间值也就出现错误,因此我们要采用一个更加温和的计算公式,来防止这种情况的发生
如图,先将right-left并除以2,再加上left,便不会造成溢出,所以使用该公式会更加完美。
3. 二分法查找函数
将二分法进行一个封装成一个函数,这样在进行二分法查找时,可以直接调用更加方便,简洁
代码示例:
#include <stdio.h>
#include <string.h>
int binary_search(int arr[], int k, int sz)//此即为封装的函数
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] < k)
{
left = mid + 1;
}
else if (arr[mid] > k)
{
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
//当循环之后也没有找到,那样不知道程序是什么情况因此返回-1,表示没有找到;
}
int main()
{
int arr[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int k = 0;
scanf("%d", &k);
int sz = sizeof(arr) / sizeof(arr[0]);
//找到了就返回下标,找不到就返回-1
int ret = binary_search(arr, k, sz);//调用函数
if (-1 == ret)
printf("找不到\n");
else
printf("找到了,下标是:%d\n", ret);
return 0;
}
文章结束啦!咱们下期见😃
如有纰漏还请各位斧正,如果对你有帮助的话,创作不易希望能留下 ”点赞“、”关注“、”评论”三连支持一下博主!🙈
如果有什么疑问或不同的见解,欢迎评论区留言嗷👀,大家一起学习,共同进步! 😉