当前位置:首页算法 > 正文

二分法

作者:野牛程序员:2023-02-25 13:20:17算法阅读 2665

二分法(Binary Search)是一种常用的搜索算法,可以用来查找有序数组中的元素。它的思想非常简单:将有序数组分成两半,判断要查找的元素在左半边还是右半边,然后递归查找。

具体实现时,我们需要记录数组的左边界 $l$ 和右边界 $r$,以及要查找的元素 $x$。每次取数组的中间位置 $mid=(l+r)/2$,将数组分成两半:左半部分为 $[l, mid-1]$,右半部分为 $[mid+1, r]$。然后,判断要查找的元素 $x$ 和中间位置的元素 $nums[mid]$ 的大小关系,如果 $x>nums[mid]$,说明要查找的元素在右半部分,否则在左半部分。如果相等,说明找到了,返回中间位置 $mid$。如果左右两半部分都没找到,说明要查找的元素不存在于数组中。

下面是用 C++ 实现的二分查找算法:

#include <iostream>
#include <vector>

using namespace std;

int binary_search(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size() - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    return -1;
}

int main() {
    vector<int> nums = {1, 3, 5, 7, 9};
    int target = 7;

    int idx = binary_search(nums, target);

    if (idx != -1) {
        cout << "Target " << target << " found at index " << idx << endl;
    } else {
        cout << "Target " << target << " not found" << endl;
    }

    return 0;
}

在这个例子中,我们首先定义了一个有序数组 nums 和要查找的元素 target。然后,我们调用 binary_search 函数查找元素在数组中的位置。如果找到了,输出元素的位置;否则输出未找到的提示。

需要注意的是,二分法要求数组是有序的。如果数组无序,需要先进行排序。此外,二分法的时间复杂度是 $O(\\log n)$,其中 $n$ 是数组的长度。因为每次查找都会将数组的大小减半,所以最多只需要查找 $\\log n$ 次,即可将数组查找完毕。


二分法还有一些变种,例如左侧二分、右侧二分、插入位置查找等。

左侧二分:

左侧二分的思想是,在找到目标值时,继续向左寻找,直到找到第一个小于目标值的位置。

int left_binary_search(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size() - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    if (left < nums.size() && nums[left] == target) {
        return left;
    } else {
        return -1;
    }
}


右侧二分:

右侧二分的思想是,在找到目标值时,继续向右寻找,直到找到第一个大于目标值的位置。

int right_binary_search(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size() - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] <= target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    if (right >= 0 && nums[right] == target) {
        return right;
    } else {
        return -1;
    }
}

插入位置查找:

插入位置查找的思想是,在没有找到目标值时,返回目标值应该插入的位置。

int searchInsert(vector<int>& nums, int target) {
    int left = 0;
    int right = nums.size() - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    return left;
}


这些变种都是在原有二分法的基础上进行的简单修改,但是要注意边界条件的处理。左侧二分和右侧二分可以用于求解一些特定的问题,例如计算逆序对数量。而插入位置查找可以用于在有序数组中插入一个元素,使得数组仍然有序。


除了基本的二分法和变种之外,还有一些高级的二分法算法,例如倍增算法、三分法、计算几何中的二分答案等。

倍增算法:

倍增算法通常用于解决静态区间问题,例如求解静态区间最大值、最小值、区间和等问题。倍增算法的核心思想是预处理出所有可能的区间大小,然后利用已经处理出的区间大小求解更大的区间大小。

const int N = 1e5 + 5;

int n, m;
int a[N];
int f[N][30];

void init() {
    for (int i = 1; i <= n; i++) {
        f[i][0] = a[i];
    }
    for (int j = 1; (1 << j) <= n; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        }
    }
}

int query(int l, int r) {
    int k = log2(r - l + 1);
    return max(f[l][k], f[r - (1 << k) + 1][k]);
}

三分法:

三分法通常用于解决单峰函数的最值问题。单峰函数是指在某个位置左侧单调递增,在某个位置右侧单调递减的函数。三分法的核心思想是通过二分的方式确定函数的峰值所在的范围,然后在峰值范围内继续三分求解最值。

double func(double x) {
    return -x * x + 10 * x + 2;
}

double ternary_search(double left, double right, double eps) {
    while (right - left > eps) {
        double m1 = left + (right - left) / 3.0;
        double m2 = right - (right - left) / 3.0;
        if (func(m1) < func(m2)) {
            left = m1;
        } else {
            right = m2;
        }
    }
    return (left + right) / 2.0;
}

二分答案:

二分答案是计算几何中的一种常见技巧,用于解决一些几何问题,例如判断一个点是否在多边形内部、判断两个凸包是否相交等。二分答案的核心思想是二分答案值,在每一次计算中,通过判断二分答案的值是否满足一定的条件来决定二分答案的区间。

野牛程序员教少儿编程与信息学奥赛-微信|电话:15892516892
野牛程序员教少儿编程与信息学竞赛-微信|电话:15892516892
相关推荐

最新推荐

热门点击