实现二分的两种不同方式,避免陷入死循环。

参考资料

  1. LeetCode总结,二分法一般性总结:证明正确性与应用
  2. 二分法小结
  3. 二分查找算法模板
  4. 二分查找有几种写法?它们的区别是什么?
  5. 写对二分查找不能靠模板,需要理解加练习 (附练习题,持续更新)

二分法介绍

二分查找法主要是解决在“一堆数中找出指定的数”这类问题,这“一堆数”必须有以下特征:

  • 存储在数组中
  • 有序排列

思想

假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值。

二分和单调性的关系

有单调性的一定可以二分,但可以二分的不一定有单调性,即没有单调性也可以二分。

实现

方式

递归

1
2
3
4
5
6
7
8
9
10
11
12
13
int bsearch(int array[], int low, int high, int target)
{
if (low > high) return -1;

int mid = (low + high)/2;
if (array[mid]> target)
return binarysearch(array, low, mid -1, target);
if (array[mid]< target)
return binarysearch(array, mid+1, high, target);

//if (midValue == target)
return mid;
}

stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int bsearchWithoutRecursion(int array[], int low, int high, int target)
{
while(low <= high)
{
int mid = (low + high)/2;
if (array[mid] > target)
high = mid - 1;
else if (array[mid] < target)
low = mid + 1;
else //find the target
return mid;
}
//the array does not contain the target
return -1;
}

整数二分实现

需要考虑边界问题,否则会陷入死循环。

版本1

当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。

1
2
3
4
5
6
7
8
9
10
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}

版本2

当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。

1
2
3
4
5
6
7
8
9
10
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}

浮点数二分实现

无需考虑边界问题。

  1. 找到一个区间[l, r], 使得答案一定在区间中。

  2. 找到一个判断条件,使得判断条件具有二段性,并且答案一定是该二段性的分界点。

  3. 分析中点mid = (l + r) / 2 在该判断条件下是否成立。
    如果成立,考虑答案在哪个区间;
    如果不成立,考虑答案在哪个区间。

  4. 确定更新方式r = mid 或 l = mid。

1
2
3
4
5
6
7
8
9
10
11
12
13
bool check(double x) {/* ... */} // 检查x是否满足某种性质

double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}

关于浮点数二分中区间精度的选取:

一般来说区间精度为1e-6就很高了。如果要求结果保留4位小数,那么精度可以取1e-6;如果要求结果保留5位小数,那么区间精度可以取1e-7。以此类推……

简言之,区间精度至少要比要保留的小数位数高2位。

如何理解

留言

⬆︎TOP