设array[]为一单调递增序列。
# include <bits/stdc++.h>
using namespace std;
TYPE array[SIZE]; // 单调递增序列(下标1~length)
int length; // 数组中的元素个数
二分
二分的基础用法是在单调序列或函数中进行查找。
平均时间复杂度:O(logn)(数据规模为n)。
整数集合上的二分:
保证最终答案处于闭区间[l, r]以内,循环以l=r结束。
在序列array[]中查找 >=x 的数中最小的一个:
int Successor(int a[], int x, int l, int r) {
while (l < r) {
int mid = (l+r)>>1;
if (a[mid] >= x) r = mid; else l = mid+1;
}
return a[l];
}
// main
int ans1 = Successor(array, x, 1, length);
在序列array[]中查找 <=x 的数中最大的一个:
int Predecessor(int a[], int x, int l, int r) {
while (l < r) {
int mid = (l+r+1)>>1;
if (a[mid] <= x) l = mid; else r = mid-1;
}
return a[l];
}
// main
int ans2 = Predecessor(array, x, 1, length);
- 不同需求的二分需要根据边界进行不同调整,防止死循环。
- 处理无解情况:把最初的二分区间[1, n]分别扩大为[1, n+1]和[0, n],如果最后二分终止于扩大后的越界下标上,则说明序列中不存在所求的数。
实数域上的二分:
确定所需的精度eps,以 r - l > eps 为循环进行条件。
需要保留k为小数时,可令 r - l >= 10-(k+1)。
double Dichotomy(double l, double r) {
while (r - l > eps) {
double mid = (l+r)/2;
if (calc(mid)) r = mid; else l = mid; // calc()根据需要自定义
}
return l;
}
// main
int ans = Dichotomy(左边界, 右边界);
精度不易确定时,也可采用循环固定次数的二分方法。
倍增
同样需要单调条件。
当状态空间过大,线性递推无法满足需求时,可以通过“成倍增长”的方式进行递推。(相比二分来讲,当目标位置较靠前时倍增算法更优)
平均时间复杂度:O(logn)
在序列array[]中查找 <=x 的数中最大的一个:
int Doubling(int a[], int x, int s, int e) { // 在闭区间[s, e]中求解
int p = s, d = 1;
while (d) {
if (a[p+d] <= x) p += d, d = min(e-p, d<<1);
else e = p+d - 1, d >>= 1;
}
return a[p];
}
// main
int ans = Doubling(array, x, 1, length);