C语言实现二分查找,我相信很多朋友都会,但是这里面有一个小细节你可能没有注意到。本篇博客主要讲解:
二分查找指的是:在一个有序数组中查找一个指定的数据。比如一个数组存储的是1,2,3,4,5,6,7,8,9,10
,这个数组是有序的,而我们想要在这里面查找一个数。
具体的玩法是:每次都找到中间元素,比较它和待查找元素,判断待查找元素在它左边还是右边。如果在左边,就去左边找,在右边就去右边找,具体的找法重复刚刚的步骤。
比如:还是刚刚的1~10的数组。我们想查找7。我们用left指代目前查找区间的左下标,right指代目前查找区间的右下标。一开始下标范围是0~9。
可以发现,核心的逻辑就是,每次找到[left, right]范围中间元素arr[mid]和待查找的元素k进行比较,此时有3种情况:
注意必须时刻保证left<=right,否则这个区间是无效的,自然就找不到了。
这个逻辑的具体实现如下:
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; // 有序数组
int sz = sizeof(arr) / sizeof(arr[0]); // 数组元素个数
int k = 0; // 待查找元素
scanf("%d", &k);
int left = 0;
int right = sz - 1;
while (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("没找到%d\n", k);
}
return 0;
}
将上面的逻辑改造成BinarySearch函数,如下:
int BinarySearch(int arr[], int sz, int k)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (arr[mid] < k)
left = mid + 1;
else if (arr[mid] > k)
right = mid - 1;
else
return mid;
}
return -1;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; // 有序数组
int sz = sizeof(arr) / sizeof(arr[0]); // 数组元素个数
int k = 0; // 要查找的数
scanf("%d", &k);
int ret = BinarySearch(arr, sz, k);
if (ret == -1)
{
printf("找不到%d\n", k);
}
else
{
printf("找到了,下标是:%d\n", ret);
}
return 0;
}
上面的逻辑中,有一行代码值得细说,那就是:
int mid = (left + right) / 2;
这是求2个数平均数的代码。这行代码其实有点小瑕疵,假设left和right都是21亿多一点,都没有超过int的最大值INT_MAX,但是加起来后就超过了,计算出来的结果会被截断。怎么解决这个问题呢?
int mid = left + (right - left) / 2;
这样就行了,和加起来除以2的结果是一样的,但是不存在越界的问题。
值得注意的是,/2
的效果和>>1
的效果是一样的。这可以类比,十进制的123右移一位得到12,恰好是123/10的结果。而>>
操作符的运算速度会更快一些(不过也快不了多少),所以上面的代码也可以这样写:
int mid = left + ((right - left) >> 2);
注意这里面的两对括号都不能丢,因为操作符优先级的问题。
a+((b-a)>>2)
的方式。感谢大家的阅读!