二分搜索(Binary Search)

二分搜索(Binary Search)是一种在有序数组 中快速查找目标元素的高效算法。它的核心思想是分而治之,通过不断将搜索区间对半分割来缩小范围。

算法核心思想

  1. 确定搜索区间:初始时为整个数组。

  2. 取中间元素:比较中间元素与目标值。

  3. 调整区间

    • 若中间元素等于目标值,直接返回索引。

    • 若中间元素小于目标值,说明目标在右半部分。

    • 若中间元素大于目标值,说明目标在左半部分。

  4. 重复过程:直到找到目标或区间为空。

1. 基本二分搜索(迭代版本)

cpp 复制代码
#include <stdio.h>

// 基本二分搜索 - 迭代版本
int binary_search(int arr[], int n, int target) {
    int left = 0;
    int right = n - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;  // 防止整数溢出
        
        if (arr[mid] == target) {
            return mid;  // 找到目标,返回索引
        } else if (arr[mid] < target) {
            left = mid + 1;  // 目标在右半部分
        } else {
            right = mid - 1;  // 目标在左半部分
        }
    }
    
    return -1;  // 未找到目标
}

2. 递归版本

cpp 复制代码
// 二分搜索 - 递归版本
int binary_search_recursive(int arr[], int left, int right, int target) {
    if (left > right) {
        return -1;  // 基础情况:搜索区间为空
    }
    
    int mid = left + (right - left) / 2;
    
    if (arr[mid] == target) {
        return mid;
    } else if (arr[mid] < target) {
        return binary_search_recursive(arr, mid + 1, right, target);
    } else {
        return binary_search_recursive(arr, left, mid - 1, target);
    }
}

// 递归版本的包装函数
int binary_search_recursive_wrapper(int arr[], int n, int target) {
    return binary_search_recursive(arr, 0, n - 1, target);
}

3. 常见变种实现

3.1 查找第一个等于目标值的元素

cpp 复制代码
// 查找第一个等于目标值的元素
int first_occurrence(int arr[], int n, int target) {
    int left = 0;
    int right = n - 1;
    int result = -1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        
        if (arr[mid] == target) {
            result = mid;      // 记录当前位置
            right = mid - 1;   // 继续在左半部分查找
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    return result;
}

3.2 查找最后一个等于目标值的元素

cpp 复制代码
// 查找最后一个等于目标值的元素
int last_occurrence(int arr[], int n, int target) {
    int left = 0;
    int right = n - 1;
    int result = -1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        
        if (arr[mid] == target) {
            result = mid;      // 记录当前位置
            left = mid + 1;    // 继续在右半部分查找
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    return result;
}

3.3 查找插入位置

cpp 复制代码
// 查找插入位置(第一个大于等于目标值的位置)
int search_insert_position(int arr[], int n, int target) {
    int left = 0;
    int right = n - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    return left;  // 返回应该插入的位置
}

4. 完整测试程序

cpp 复制代码
#include <stdio.h>

// 上面所有的函数实现...

// 打印数组
void print_array(int arr[], int n) {
    printf("[");
    for (int i = 0; i < n; i++) {
        printf("%d", arr[i]);
        if (i < n - 1) printf(", ");
    }
    printf("]\n");
}

int main() {
    int arr[] = {1, 3, 5, 7, 7, 7, 9, 11, 13, 15};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    printf("数组: ");
    print_array(arr, n);
    printf("\n");
    
    // 测试基本二分搜索
    int target = 7;
    int result = binary_search(arr, n, target);
    printf("基本二分搜索 - 元素 %d: ", target);
    if (result != -1) {
        printf("找到,索引 = %d\n", result);
    } else {
        printf("未找到\n");
    }
    
    // 测试递归版本
    target = 11;
    result = binary_search_recursive_wrapper(arr, n, target);
    printf("递归二分搜索 - 元素 %d: ", target);
    if (result != -1) {
        printf("找到,索引 = %d\n", result);
    } else {
        printf("未找到\n");
    }
    
    // 测试第一个出现位置
    target = 7;
    result = first_occurrence(arr, n, target);
    printf("第一个 %d 出现的位置: ", target);
    if (result != -1) {
        printf("索引 = %d\n", result);
    } else {
        printf("未找到\n");
    }
    
    // 测试最后一个出现位置
    result = last_occurrence(arr, n, target);
    printf("最后一个 %d 出现的位置: ", target);
    if (result != -1) {
        printf("索引 = %d\n", result);
    } else {
        printf("未找到\n");
    }
    
    // 测试插入位置
    int test_values[] = {0, 4, 7, 8, 16};
    int test_count = sizeof(test_values) / sizeof(test_values[0]);
    
    printf("\n插入位置测试:\n");
    for (int i = 0; i < test_count; i++) {
        int pos = search_insert_position(arr, n, test_values[i]);
        printf("元素 %d 应该插入的位置: %d\n", test_values[i], pos);
    }
    
    return 0;
}

5. 编译和运行

bash复制下载

复制代码
gcc -o binary_search binary_search.c

./binary_search

预期输出示例:

text复制下载

复制代码
数组: [1, 3, 5, 7, 7, 7, 9, 11, 13, 15]

基本二分搜索 - 元素 7: 找到,索引 = 4
递归二分搜索 - 元素 11: 找到,索引 = 7
第一个 7 出现的位置: 索引 = 3
最后一个 7 出现的位置: 索引 = 5

插入位置测试:
元素 0 应该插入的位置: 0
元素 4 应该插入的位置: 2
元素 7 应该插入的位置: 3
元素 8 应该插入的位置: 6
元素 16 应该插入的位置: 10

关键要点:

  1. 时间复杂度:O(log n)

  2. 空间复杂度:迭代版 O(1),递归版 O(log n)

  3. 前提条件:数组必须有序

  4. 重要技巧 :使用 left + (right - left) / 2 避免整数溢出

  5. 边界处理 :注意循环条件 left <= right 和索引更新

这些实现涵盖了二分搜索的核心算法及其常见变种,适用于不同的应用场景。

相关推荐
王哈哈^_^3 小时前
【数据集】【YOLO】【目标检测】口罩数据集,口罩佩戴识别数据集 1971 张,YOLO佩戴口罩检测算法实战训练教程。
人工智能·算法·yolo·目标检测·计算机视觉·ai·视觉检测
dragoooon344 小时前
[优选算法专题四.前缀和——NO.31~32 连续数组、矩阵区域和]
数据结构·算法·leetcode·1024程序员节
py有趣4 小时前
LeetCode算法学习之移除元素
java·数据结构·算法
一念&5 小时前
每日一个C语言知识:C 预处理器
c语言·算法
油泼辣子多加5 小时前
【实战】自然语言处理--长文本分类(2)BERTSplitLSTM算法
算法·自然语言处理·分类
WWZZ20255 小时前
快速上手大模型:深度学习2(实践:深度学习基础、线性回归)
人工智能·深度学习·算法·计算机视觉·机器人·大模型·slam
初级炼丹师(爱说实话版)5 小时前
算法面经常考题整理(1)机器学习
人工智能·算法·机器学习
被AI抢饭碗的人6 小时前
算法题(246):负环(bellman_ford算法)
算法
大数据张老师7 小时前
数据结构——折半查找
数据结构·算法·查找·折半查找