二分查找
基于分治策略的一种查找方法, 时间复杂度 $ O(log_{2}{n}) $
是用于解决单调有序问题,缩小问题规模
算法步骤:
- 将左指针指向序列首元素,右指针指向队尾元素
- 中间指针左指针右指针中间指针 = 左 指 针 + 右 指 针 2 \frac{左指针 + 右指针}{2} 2左指针+右指针
- 若中间指针指向的值大于目标值,则我们向左继续查找,右指针 = 中间指针 - 1
- 若中间指针指向的值小于目标值,则我们向右基础查找,左指针 = 中间指针 + 1
- 以此方法递归求解问题即可
代码演示:
int binary_search1(int *num, int n, int x) {
int head = 0, tail = n - 1, mid;
while (head <= tail) {
mid = (head + tail) >> 1;
if (num[mid] == x) return mid;
if (num[mid] < x) {
head = mid + 1;
}else {
tail = mid - 1;
}
}
return -1;
}
二分问题模型:
- 11110000问题(满足特定条件的最小值,最后的 1)
- 00001111问题(满足特定条件的最大值,最先的 1)
11110000问题:
首先我们需要明确我们的目的是为了不断缩小问题规模,也就是说最终答案已经要包含在我们的问题区间内。
像二分法一样我们进行二分查找,我们通过中间指针指向的值来缩小问题区间,如果我们发现中间值为0,那么我们的右指针可以放心大胆的跨度到中间指针前一位。如若我们发现中间值为1,那我们不能确定这个值是否为最终结果,那我们只能将左指针指向中间指针的位置。
我们需要注意全1并为偶数的情况, 如若现在序列为 11, 也就是序列中间指针都指向中间靠左的元素,那么右指针将永远不会更改,最终造成死循环
解决方法: $中间指针左指针右指针 中间指针 = \frac {左指针 + 右指针 + 1}{2} $
每次将中间指针指向中间靠右的元素将会解决这个问题。
代码演示:
int binary_search2(int *num, int n, int x) {
int head = -1, tail = n - 1, mid;
while (head < tail) {
mid = (head + tail + 1) >>1;
if (num[mid] == 1) {
head = mid;
} else {
tail = mid - 1;
}
}
return head;
}
00001111问题:
和上述问题我们要达到的目的一样也是为了不断的缩小问题规模。
那么求解问题的方法和求解00001111也是不禁相同。当我们发现中间值为0时,我们知道中间值不满足条件,那么我们的左指针可以放心大胆的向右前进,左指针 = 中间指针 + 1。当我们发现中间值为1时,我们不能保证这个是问题答案,那我们右指针只能小心翼翼的向左前进。右指针 = 中间指针。
由于我们就要取靠左的值所以正常求解即可
代码演示:
int binary_search3(int *num, int n, int x) {
int head = 0, tail = n, mid;
while (head < tail) {
mid = (head + tail) >> 1;
if (num[mid] == 1) {
tail = mid;
} else {
head = mid + 1;
}
}
return head == n ? -1:head;
}
三分查找
适用于解决单峰问题,要保证L与R在问题答案的左右两侧。
代码演示:
#include <stdio.h>
#include <math.h>
#define EPS 1e-6
double f(double x, double a, double b, double c) {
return a*x*x + b*x + c;
}
double three_point_search(double a, double b, double c) {
double head = -10000, tail = 10000, m1, m2;
if (a > 0) {
a = -a, b = -b, c = -c;
}
while (fabs(tail - head) > EPS) {
m1 = (tail - head) / 3.0 + head;
m2 = (tail - head) / 3.0 * 2 + head;
if (f(m1,a,b,c) < f(m2, a, b, c)) {
head = m1;
}else {
tail = m2;
}
}
return head;
}
int main() {
double a, b, c;
while (~scanf("%lf %lf %lf", &a, &b, &c)) {
printf("%f\n", three_point_search(a, b, c));
}
}