二分法与三分法

二分查找

基于分治策略的一种查找方法, 时间复杂度 $ 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));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值