基础二分
下面是一个最基础的二分搜索代码,从一个数组(数据从小到大排)中找出某元素。
#include <stdio.h>
// 函数声明
int binarySearch(int arr[], int left, int right, int x);
int main() {
// 测试数据
int arr[] = {2, 3, 4, 10, 40};
int n = sizeof(arr) / sizeof(arr[0]);
int x = 10; // 要查找的元素
// 调用二分搜索函数
int result = binarySearch(arr, 0, n - 1, x);
// 输出结果
if(result == -1)
printf("元素 %d 不在数组中\n", x);
else
printf("元素 %d 的索引是 %d\n", x, result);
return 0;
}
// 二分搜索函数定义
int binarySearch(int arr[], int left, int right, int x) {
while (left <= right) {
int mid = (left+right) / 2; // 计算中间索引
// 检查 x 是否在中间
if (arr[m] == x)
return m;
// 如果 x 大于中间元素,则忽略左侧
if (arr[m] < x)
left = m + 1;
else
right = m - 1;
}
// 元素不存在
return -1;
}
二分变式一
我们希望找出不小于某数x的最小数(不大于某数x的最大数也是同理),数据从小到大排。
这时要注意,在分析的时候我们可以分成三类情况来讨论:
1、找到了x,这时我们应该继续往右走,右边才是我们要找的
2、比x大,这时左边可能要往左走,但是可能此时这个就是我们要找的
3、比x小,继续往右走
根据这三种情况我们修改代码:
#include<stdio.h>
int min_ge(int *A, int n, int x){
int left = 0, right = n, mid;
while (left<right){
mid = (left + right)/2;
if (A[mid]>x)right = mid;
else left = mid + 1;
}
return left;
}
int main(){
int A[] = {8,10,33,49,55,63,63,70,85,85};
int x;
printf("请输入要查找的数下限:");
scanf("%d",&x);
int p = min_ge(A,10,x);
if (p<10) printf("第一个数A[%d] = %d\n",p,A[p]);
else printf("不存在这样的数");
return 0;
}
还有两个改动的点:
1、循环条件变为left<right,无需取等,取等的位置已经计算过了
2、返回值为left或者right都行,因为此时两者相等
二分变式二
有一组木棍,我们希望切成K根,每根长度一样,我需要找到最长能切的长度。
这里变式在于寻找的是一个长度,而不是数组里的一个值。
要注意:
1、确定搜索空间,是从1到次长+1
2、编写check函数确认是否满足题目条件。
/*
计算最长木棍的切割方案
*/
#include<stdio.h>
int check(int *A, int n, int K, int mid) {
int sum = 0;
for (int i = 0; i < n; i++) { // 数组下标从0开始
sum += A[i] / mid;
if (sum >= K) return 1;
}
return 0; // 这里不需要else,直接返回0
}
int long_stick_cut(int *A, int n, int K){
int left = 1, right = A[n - 1]+1, mid;
while (left < right) {
mid = (left + right) / 2;
if (check(A, n, K, mid)) {
left = mid + 1;
} else {
right = mid;
}
}
return left-1;
}
int main(){
int A[10] = {8, 10, 33, 49, 55, 63, 63, 70, 85, 85};
printf("L = %d\n", long_stick_cut(A, 10, 32));
return 0;
}
思考问题
(某大厂/考研面试题)二分为什么要(left+right)/2呢?我不能随机在[left,right]之间取一个值吗?