二分通常指的是二分查找(Binary Search),它是一种高效的查找算法,用于在有序数组中查找某一特定元素的位置。二分查找的思路是:
- 每次取中间位置的元素与目标值进行比较。
- 如果中间位置的元素正好等于目标值,则查找成功。
- 如果中间位置的元素大于目标值,则在数组的左半部分继续查找。
- 如果中间位置的元素小于目标值,则在数组的右半部分继续查找。
- 重复上述过程,直到找到目标值或查找范围为空。
一.数的范围
题目
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回 -1 -1
。
代码
#include<iostream>
using namespace std;
const int N = 1e5 + 5;
int arr[N];
int n, q;
int main()
{
int k = 0;
scanf("%d%d", &n, &q);
for (int i = 0; i < n; i++) scanf("%d", &arr[i]);
while (q--)//循环q次
{
scanf("%d", &k);//查询的数
int l = 0, r = n - 1;//首尾
while (l < r)//l=r时停止
{
int mid = l + r >> 1;//中点
if (arr[mid] >= k) r = mid;//中值>=k,缩小范围0到mid
else l = mid + 1;//中值<=k,缩小范围mid+1到n-1
}//以此类推求出左边界
if (arr[l] != k)//如果剩下的一个不是k返回-1 -1
{
printf("-1 -1\n");
continue;//结束本次循环
}
else printf("%d ", l);//是k,输出序号
r = n - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;//加1向上取整,求左边界向下,求右边界向上对称
if (arr[mid] <= k) l = mid;//对称
else r = mid - 1;//对称
}
printf("%d\n", l);//输出右边界
}
return 0;
}
解析
while (l < r)//l=r时停止
{
int mid = l + r >> 1;//中点
if (arr[mid] >= k) r = mid;//中值>=k,缩小范围0到mid
else l = mid + 1;//中值<=k,缩小范围mid+1到n-1
}//以此类推求出左边界
1.查找左边界,中点值与查找值比较然后缩小范围,中间值大右边界缩小到 mid,中间值左边界缩小到mid + 1,循环直到 l = r,
if (arr[l] != k)//如果剩下的一个不是k返回-1 -1
{
printf("-1 -1\n");
continue;//结束本次循环
}
else printf("%d ", l);//是k,输出序号
2.判断结束时对应的数是不是要查找的数,不是的话输出 -1 停止本次循环,是的话输出左边界 l
r = n - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;//加1向上取整,求左边界向下,求右边界向上对称
if (arr[mid] <= k) l = mid;//对称
else r = mid - 1;//对称
}
3.求右边界,更新 r,求左边界到末尾的右边界,求法与左边界类似,对称, l + r + 1 >> 1 向上取整,因为下一步 if 判断条件是 <=,到最后一步剩两个元素的时候向下取整会发生死循环,即
l+1=r
l<r
mid=l
q[mid]<=x
l=mid=l
二、三次方根
题目
给定一个浮点数 n,求它的三次方根。(-10000<n<10000)
代码
1.二分法
#include<iostream>
using namespace std;
int main()
{
double x;
scanf("%lf", &x);
double l = -10000, r = 10000;
while (r - l > 1e-8) //精度
{
double mid = (l + r) / 2;//取中点
if (mid * mid * mid >= x) r = mid;//mid在右,替换r
else l = mid;
}
printf("%lf\n", l);
return 0;
}
2.牛顿迭代法
#include<iostream>
#include<cmath> //绝对值函数abs
using namespace std;
int main()
{
double n;
scanf("%lf", &n);
double x = n, prev_t = x - 1;
while (abs(x - prev_t) > 1e-8)//精度
{
prev_t = x;
x = (2 * x * x * x + n) / (3 * x * x);//牛顿迭代
}
printf("%lf\n", x);
return 0;
}
解析
1.二分法
double l = -10000, r = 10000;
n 的范围
while (r - l > 1e-8) //精度
{
double mid = (l + r) / 2;//取中点
if (mid * mid * mid >= x) r = mid;//mid在右,替换r
else l = mid;
}
循环条件 l 与 r 的差值大于10的-8次方,为所求值的精度,通过中点值的3次方与所开值比较所小范围,直到循环结束得到所求值
2.牛顿迭代法
根据迭代公式
求解 的根
迭代求解即可
double x = n, prev_t = x - 1;
设定两个变量分别储存迭代前和迭代后的结果,其中x可为任意正整数
while (abs(x - prev_t) > 1e-8)//精度
{
prev_t = x;
x = (2 * x * x * x + n) / (3 * x * x);//牛顿迭代
}
循环条件:迭代后的值-迭代前的值>10的-8次方