目录
一、整数二分
模板
右边界二分查找(左开右闭)
左边界二分查找(左闭右开)
应用
数的范围
二、浮点数的二分
模板
应用
算术平方根
三、习题
1.数的三次方根
一、整数二分
tip:满足单调性的数组一定可以使用二分查找,但可以使用二分查找的数组不一定需要满足单调性。
假设我们找到了条件 C1,和它的对立条件 C2,就能够将数组a二分,如下图所示:
观察上图可以发现,索引3和索引4这两个位置都可以作为 C1 和 C2 的分界点。其中,索引3是红色区域的右边界,索引4是绿色区域的左边界。而我们接下来要讨论的二分查找模板就是用来寻找 C1 和 C2 的分界点的。
模板
右边界二分查找(左开右闭)
需注意l + r + 1的条件, 否则当l = r - 1时,会导致无限循环。
int right_bound(int l, int r) {
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
左边界二分查找(左闭右开)
int left_bound(int l, int r) {
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
应用
数的范围
tip:找好需要寻找的左边界和右边界。
建议通过区间图的方式,来决定求左边界还是右边界。
void Solution()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) scanf("%d", &q[i]);
while (m--)
{
int x, l = 0, r = n - 1;
scanf("%d", &x);
while (l < r)
{
int mid = (l + r) >> 1;
if (q[mid] >= x) r = mid;
else l = mid + 1;
}
if (q[l] != x) std::cout << "-1 -1" << std::endl;
else
{
std::cout << l << ' ';
l = 0, r = n - 1;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (q[mid] <= x) l = mid;
else r = mid - 1;
}
std::cout << l << std::endl;
}
}
}
二、浮点数的二分
浮点数的二分法与整数二分法同理,但是可以不用注意边界问题。
模板
int bound(int l, int r) {
while (l - r > 1e-6) {
int mid = (l + r + 1) >> 1;
if (check(mid)) l = mid;
else r = mid;
}
return l;
}
应用
算术平方根
根据经验,保留小数点后几位数,就将循环条件中的极小数往减小2位。
例如:
保留6位小数,将 while (r - l > 1e-8) 中写为1e-8。
保留4位小数,将 while (r - l > 1e-6) 中写为1e-6。
void Solution2()
{
double x;
scanf("%lf", &x);
double l = 0.0, r = x;
while (r - l > 1e-8)
{
double mid = (r + l) / 2;
if (mid * mid >= x) r = mid;
else l = mid;
}
std::cout << l << std::endl;
}
三、习题
1.数的三次方根
void Solution()
{
double x;
scanf("%lf", &x);
double l = 0.0, r = x;
while (r - l > 1e-8)
{
double mid = (l + r) / 2.0;
if (mid * mid * mid >= x) r = mid;
else l = mid;
}
std::cout << l << std::endl;
}