C语言-二分查找

引言

在1个升序的数组中查找指定的数字n,很容易想到的办法就是遍历数组,但是这种方法效率较低。此时可以使用二分查找,二分查找适合有序或排序过的数组

目录

引言

什么是二分查找?

引言题目代码

为什么两个公式等价

二分查找有两个限制条件

二分查找的写法

左闭区--右闭区

左闭区--右开区


什么是二分查找?

二分查找(Binary Search),又称折半查找,是一种在有序数组中查找特定元素的高效算法。二分查找的过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则搜索过程将在数组的大于或小于中间元素的那一半区域中继续,以此类推,直到找到要查找的元素,或者剩下的半区域为空。


引言题目代码

#include <stdio.h>

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
// 这可以安全计算,不会溢出

二分查找有两个限制条件

  1. 查找的数量只能是一个,不能是多个
  2. 查找的对象在逻辑上必须是有序的

二分查找的写法

左闭区--右闭区

就是我上边引言的代码

左闭区--右开区
#include <stdio.h>

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;
}

  • 16
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值