二分查找
上面我们学了简单查找,并且可以判断输入的数据相比于目标值是大了还是小了。那么举个例子:假如给定100个元素:
那么假设我们猜的第一个数字是50,如果返回的数据是“猜小了”的话,我们就知道从1~50这些数据都是小于目标值的,那么我们就可以舍去这个范围内所有的数,下一次就在(50~100)中间这个范围内猜:
那么如果我们下一次猜的数字是75,如果返回的数字是“猜大了”的话,同样,我们就能知道在(75~100)这个范围内的所有数字都是大于目标值的。那么我们就可以在(50~75)这个范围内猜数。以此类推,每一次猜的数都是在此范围内的中间值,而每次都可以排除掉一半的数据,这就是二分查找,下面是实现二分查找的代码举例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
srand(time(0));
int target = rand() % 100 + 1;
int left = 1; //设置左边界
int right = 100; //设置有边界
while (left <= right) { //当左右边界相等时,即为目标值
int mid = (right - left) / 2 + left; //数值过大的时候(right + left) / 2不可用
if (mid == target) {
printf("猜对了");
return 0;
}
else if (mid < target) {
printf("猜小了");
left = mid + 1; //如果目标值大于所猜的中间值,则小于等于中间值的部分均不要
}
else if (mid > target) {
printf("猜大了"); //如果目标值小于所猜的中间值,则大于等于中间值的部分均不要
right = mid - 1;
}
}
return 0;
}
此段代码中,我们设置了左边界left和有边界right,即在左边界和有边界中间取中间值查找中间值mid = (right - left)/2 + left而使用这种方法设定mid的原因我们在代码中也体现了;当中间值mid小于目标值target时,小于等于mid的值舍去,此时左边界left = mid + 1,而当中间值mid大于目标值target时,大于等于mid的值舍去,此时右边界变right = mid - 1以此类推,当找到中间值时或左边界等于有边界时(这种情况就是所有数都猜完了,就剩最后一个数了此时这个数就是目标数target),输出结果,结束循环。
一般来说在一个有n个元素的列表中,使用二分查找最多需要步,而简单查找则最多需要n步。
二分查找是一个典型的算法,输入是一个有序地元素列表,二分查找的时间复杂度为O()。
例:给定一个升序的数组,含有n个元素,现在有一个目标值target,我们需要在数组中找到目标值,并返回下标,下面是代码:
#include<stdio.h>
#include<stdlib.h>
int Binsearch(int* arr, int target, int n) { //传递参数有数组,目标值,数组元素数量
int left = 0; //定义左右变量
int right = n - 1;
while (left <= right) {
int mid = (right - left) / 2 + left;
if (arr[mid] == target) {
return mid;
}
else if (arr[mid] < target) {
left = mid + 1;
}
else {
right = mid - 1;
}
}
return -1; //若没有与目标数target匹配的数组元素,返回值-1
}
int main() {
int n;
scanf("%d", &n); //输入数组元素个数
int* arr = (int*)malloc(sizeof(int) * n); //申请堆空间,大小根据数组元素个数而定,为动态空间
for (int i = 0; i < n; i++) { //输入数组各个元素
scanf("%d", &arr[i]);
}
int target; //定义目标值
scanf("%d", &target); //输入目标值
int k = Binsearch(arr, target, n); //设置二分查找函数,返回值为int类型,返回数据为和目标值相等的数组元素的下标
printf("%d", k); //输出数组元素
free(arr); //解放堆区空间
return 0;
}