在1个升序的数组中查找指定的数字n,很容易想到的办法就是遍历数组,但是这种方法效率较低。此时可以使用二分查找,二分查找适合有序或排序过的数组
目录
引言
什么是二分查找?
引言题目代码
为什么两个公式等价
二分查找有两个限制条件
二分查找的写法
左闭区--右闭区
左闭区--右开区
二分查找(Binary Search),又称折半查找,是一种在有序数组中查找特定元素的高效算法。二分查找的过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则搜索过程将在数组的大于或小于中间元素的那一半区域中继续,以此类推,直到找到要查找的元素,或者剩下的半区域为空。
#include
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int left = 0;
int right = sizeof(arr) / sizeof(arr[0]) - 1;
int key = 7; // 要找的数字
int mid = 0; // 记录中间元素的下标
int find = 0;
while (left <= right) {//注意条件,有=号,否则无论答案对否都会跳出显示找不到
mid = (left + right) / 2;
if (arr[mid] > key) {
right = mid - 1;
} else if (arr[mid] < key) {
left = mid + 1;
} else {
find = 1;
break;
}
}
if (1 == find) {
printf("找到了,下标是%d\n", mid);
} else {
printf("找不到\n");
}
return 0;
}
求中间元素的下标, mid = (left+right)/2 ,如果left和right比较大的时候可能存在问 题,可以使用这个公式防止溢出:mid = left+(right-left)/2
这个公式等价是我在学习二分查找的时候最有疑问的一个点。现在我来讲一下
先假设我们有以下数组和要查找的键值:
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int key = 7;
初始时,left 和 right 分别设置为数组的起始和结束索引:
int left = 0; // 数组的起始索引
int right = sizeof(arr) / sizeof(arr[0]) - 1; // 数组的结束索引
在第一次循环中,我们将计算 mid:
使用原始表达式:
mid = (left + right) / 2;
mid = (0 + 9) / 2;
mid = 9 / 2;
mid = 4;
使用修改后的表达式:
mid = left + (right - left) / 2;
mid = 0 + (9 - 0) / 2;
mid = 0 + 9 / 2;
mid = 0 + 4;
mid = 4;
现在,假设 left 和 right 的值非常大,接近 int 类型的最大值 INT_MAX。如果我们使用原始表达式,left + right 可能会导致溢出:
// 假设 left 和 right 非常接近 INT_MAX
left = INT_MAX - 100;
right = INT_MAX - 50;
// 使用原始表达式
mid = (left + right) / 2;
// 如果不发生溢出,结果应该是 (INT_MAX - 100 + INT_MAX - 50) / 2
// 但实际上,left + right 会溢出,导致错误的结果
如果我们使用修改后的表达式,就不会发生溢出,因为 right - left 不会超过 int 类型的最大值:
// 使用修改后的表达式
mid = left + (right - left) / 2;
// 结果是 (INT_MAX - 100) + ((INT_MAX - 50) - (INT_MAX - 100)) / 2
// 这可以安全计算,不会溢出
就是我上边引言的代码
#include
int main() {
// 示例数组,实际使用时可以从文件、命令行参数或用户输入获取
int nums[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int target = 4; // 需要查找的目标值
int numsSize = sizeof(nums) / sizeof(nums[0]); // 计算数组大小
// 初始化左边界和右边界
int left = 0;
int right = numsSize; // 注意:右边界是开区间,所以是数组大小
// 二分查找循环
while (left < right) { // 左闭右开,所以使用小于号
int mid = left + (right - left) / 2; // 计算中间位置
if (nums[mid] == target) {
// 找到目标值,返回索引
printf("目标值 %d 在数组中的索引为 %d。\n", target, mid);
return 0; // 程序结束
} else if (nums[mid] < target) {
// 目标值在右区间,调整左边界
left = mid + 1;
} else {
// 目标值在左区间,调整右边界
right = mid;
}
}
// 未找到目标值
printf("目标值 %d 未在数组中找到。\n", target);
return 0;
}