二分查找也称折半查找,它是一种效率较高的查找方法
例:在一个有序数组中查找具体的某个数字n,
如,在一个一维数组中存储了一组有序元素1 2 3 4 5 6 7 8 9 10,现在我们要查找数字7,有人会想从前往后遍历去查找,这样固然可行,但是如果数据元素不是10个,而是一千甚至一万,则这样的查找效率显得很低
这里我们忽略了这组元素是有序的,我们可以从这组数据的中间开始查找,取中间的元素与要查找的元素比较大小,这样一次比较可以排除一半的元素,显然效率比遍历更高,这就是我们要说的二分查找,又称为折半查找
如在1 2 3 4 5 6 7 8 9 10查找7
我们查找的思路如下:
首先找到这组元素的中间元素:最左边元素的下标和最右边元素的下标之和除以2,作为中间元素的下标,如本例,(9+0)/ 2 = 4,4作为中间元素的下标,对应的数字是5
5比要找的数字7小,被查找的范围变为6 7 8 9 10;
新的被查找的范围的中间元素的下标(5+9)/ 2 = 7,对应的数字是8
8比要找的数字8大,被查找的范围6 7;
新的被查找的范围的中间元素的下标(5+6)/ 2 = 5,对应的数字是6
6比要找的数字7小,被查找的范围7;
新的被查找的范围的中间元素的下标(6+6)/ 2 = 6,对应的数字是7,找到了
我们发现上面算法只找了4次就找到了所要找的元素,如果是遍历的话,最坏的情况要找10次,所以二分查找其效率较高,其时间复杂度为
用代码实现:
#include
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int k = 7; //要查找的数字
//在数组中查找k
int sz = sizeof(arr) / sizeof(arr[0]); //数组元素个数
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,其下标是:%d\n",arr[mid],mid);
break;
}
}
if(left > right)
{
printf("找不到\n");
}
return 0;
}
我们运行可以得到:
当把k改为11 ,我们发现找不到
如果上述代码要用函数实现,我们需要注意
如果函数内部需要参数部分传递某个数组元素的个数,数组元素个数要在函数外部求
如果在函数内部求数组元素个数,因为数组名传参传递的是数组名的首元素地址,此时sizeof(arr)算出的结果为4,sizeof(arr) / sizeof(arr[0])算出的结果为1,而不是元素个数,这点需要注意
用函数实现一个整型有序数组的二分查找:
#include
int binary_search(int a[], int k, int s)
{
int left = 0;
int right = s - 1;
while(left <= right)
{
int mid = (left + right) / 2;
if(k < a[mid])
{
right = mid - 1;
}
else if(k > a[mid])
{
left =mid + 1;
}
else
{
return mid;
break;
}
}
return -1;
}
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int k = 0;
printf("输入要查找的数:");
scanf("%d",&k);
int sz = sizeof(arr) / sizeof(arr[0]);
//找到就返回找到的位置的下标
//找不到就返回-1
int ret = binary_search(arr, k, sz);
if (ret == -1)
{
printf("找不到\n");
}
else
{
printf("找到了:%d,下标是:%d\n",arr[ret],ret);
}
return 0;
}
运行结果如下: