目录
一、为什么要使用二分查找?
题目:在一个有序数组中查找具体的某个数字n,并打印其下标。
在看到以上题目时,我们通常会使用遍历数组的方法来找到该元素,如下:
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; //有序数组
int n = 7; //要查找的数字
int i = 0;
int flag = 0; //标记变量,判断是否找到该数字
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i++)
{
if (arr[i] == n)
{
printf("找到了,该数字的下标是%d\n", i);
flag = 1;
break;
}
}
if (flag == 0)
{
printf("找不到该数字");
}
return 0;
}
这个方法是可行的,但如果数组元素非常非常多的时候,该方法的效率就变低了。
并且我们可以注意到题目给出的数组是有序的,我们可以将这个条件利用起来,通过二分查找法(也称折半查找法)来提高效率。
二、什么是二分查找?
二分查找就是首先将数组的中间元素与要查找的数字进行比较,如果数字比中间元素小,我们接下来就在左半部分继续找该数字,如果数字比中间元素大,我们接下来就在右半部分继续找该数字,直到找到该数字为止。
三、二分查找的过程演示
首先,我们要确定二分查找的左边界和右边界。因为我们要打印的是查找的数字在有序数组中的下标,所以用数组的下标来表示边界,将0作为左边界,右边界则是数组元素个数-1。中间元素的下标则为(左边界+右边界)/2。
第一轮比较后,我们可以发现中间元素5比要找的数字7小,所以我们接下来在右半部分,即数字6~10之间继续找7。此时,左边界就变为下标5,即(mid+1),右边界不变。中间元素还是(left+right)/2。
第二轮比较后,我们可以发现中间元素8比要找的数字7大,所以我们接下来要在左半部分,即数字6~7之间继续找7。此时,左边界不变,右边界变为下标6,即(mid-1)。中间元素还是(left+right)/2。
第三轮比较后,我们发现中间元素6比要找的数字7小,所以我们接下来要在右半部分继续找7。此时,左边界变为下标6,即(mid+1),右边界不变。中间元素还是(left+right)/2。
第四轮比较结束,我们找到了数字7,其下标为6。
由此可见,使用二分查找法,只需要四轮就可以将该数字找出来,而如果使用的是遍历数组的方法,则需要7轮才能找到。如果查找的数字在数组中靠后的位置或数组元素多的话,遍历次数就更多了。
在比较的过程中,我们可以思考一个问题:left和right都是int类型的数据,而int类型的数据是有取值范围的,如果left和right都是一个非常大的数字的话,相加后就有可能发生溢出,导致mid的数值出错。那我们该如何解决这个问题呢?
我们可以将(right-left)多出来的部分除以2后,拿到left这块来,这样就可以避免溢出的情况。
最后我们可以得到:mid = left + (right-left) / 2
我们要注意的一点是,使用二分查找/折半查找的前提条件:数组是有序的
- 通过上面的过程演示,我们可以知道二分查找中比较数值的过程是有多轮的,所以应该将其写成一个循环,且循环的条件为 left<=right。
- 在进入循环前,我们要先确定二分查找的左右边界,左边界为数组下标0,右边界为数组下标的最后一个数字,即元素个数-1。
- 在每一轮的比较中,mid的值都在不断改变,所以我们应该将mid的赋值写在循环体内。
- 然后将下标为mid的元素与要查找的数字进行比较,如果arr[mid]小于该数字,接下来就改变left的赋值,在右半部分继续找该数字,如果arr[mid]大于该数字,接下来就改变right的赋值,在左半部分继续找该数字。
- 在查找该数字的过程中,为了避免数值的溢出,mid = left + (right-left) / 2
最后,我们可以再对代码做一个小小的改进:将有序数组和查找的数字都从键盘输入。
四、代码呈现
题目:在一个有序数组中查找具体的某个数字n,并打印其下标。
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int n = 0;
printf("请输入数组: ");
for (int i = 0; i < 10; i++) //输入有10个元素的有序数组
{
scanf("%d", &arr[i]);
}
printf("请输入要查找的数字: ");
scanf("%d", &n); //输入要查找的数字
int left = 0; //初始左边界
int right = sizeof(arr) / sizeof(arr[0]) - 1; //初始右边界
int flag = 0; //标记变量,判断是否找到该数字
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] < n)
{
left = mid + 1;
}
else if (arr[mid] > n)
{
right = mid - 1;
}
else
{
printf("找到了,该数字的下标是%d\n", mid);
flag = 1;
break;
}
}
if (flag == 0)
printf("找不到该数字\n");
return 0;
}
以上是我对C语言中二分查找法/折半查找法的理解。
希望可以帮到大家!如果有写的不好的地方也希望大家指出,谢谢!