二分搜索(Binary Search)是一种在有序数组 中快速查找目标元素的高效算法。它的核心思想是分而治之,通过不断将搜索区间对半分割来缩小范围。
算法核心思想
-
确定搜索区间:初始时为整个数组。
-
取中间元素:比较中间元素与目标值。
-
调整区间:
-
若中间元素等于目标值,直接返回索引。
-
若中间元素小于目标值,说明目标在右半部分。
-
若中间元素大于目标值,说明目标在左半部分。
-
-
重复过程:直到找到目标或区间为空。
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
关键要点:
-
时间复杂度:O(log n)
-
空间复杂度:迭代版 O(1),递归版 O(log n)
-
前提条件:数组必须有序
-
重要技巧 :使用
left + (right - left) / 2避免整数溢出 -
边界处理 :注意循环条件
left <= right和索引更新
这些实现涵盖了二分搜索的核心算法及其常见变种,适用于不同的应用场景。