Acwing 数的范围-二分算法-java实现
二分算法
二分算法分为整数二分和浮点数二分 浮点数二分比较容易
二分的本质不是单调性 有单调性的一定可以二分 但是能二分的不一定非得有单调性
但是二分查找需要有序性
一、原题链接
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回 -1 -1。
输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n 个整数(均在 1∼10000 范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1。
数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
二、题解
算法分析
二分算法的解释
二分算法分为两个边界 一个是 <= x 一个是>=x 的也就是上面图中的红色区域和绿色区域(为了书写区分 l = L)
红色区域
这时候mid 为图中 1 的情况 mid在红色区域 要想包含解x 则l = mid
else mid跑到绿色区域 要想包含解的话 r = mid-1;
注意: 此时的Mid = l+r +1 >> 1;
为什么是加1 呢 ? 这是因为 / 是向下取整的 如果在[mid,r]中 l = r-1 的话
这时候 mid = l + r >> 1 = L (因为是向下取整的) 那L = mid 这时候就陷入了死循环 所以要加1
mid=(l+r+1)/2 //注意这个+1,不然会出现死循环,例如l与r只差1,且check(mid)==true会出现死循环
if(check(mid)){ //check(mid)作用是判断mid是否满足红颜色的性质
//边界点在[mid,r]之间,更新方式,l=mid
}else{
//边界点在[l,mid-1]之间,更新方式,r = mid-1
}
绿色区域
这时候mid 为图中 2的情况 mid在绿色区域 要想包含解x 则r = mid
else mid跑到红色区域 要想包含解的话 l = mid+1;
注意: 此时的Mid = l+r >> 1;
mid = (l+r)/2
if(check(mid)){ //check(mid)作用是判断mid是否满足绿颜色的性质,如果满足,那么绿颜色的边界点一定在[l,mid]之间
//边界点在[l,mid]之间,更新方式为r=mid
}else{
//边界点在[mid+1,r]之间,更新方式为l = mid+1
}
代码如下(示例):
import java.io.*;
import java.util.*;
public class Main {
static final int N = 100010;
static int[] a = new int[N];
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String[] s1 = in.readLine().split(" ");
int n = Integer.parseInt(s1[0]);
int q = Integer.parseInt(s1[1]);
String[] s2 = in.readLine().split(" ");
for(int i = 0; i < n; i ++) a[i] = Integer.parseInt(s2[i]);
while(q -- > 0) {
int k = Integer.parseInt(in.readLine());
int l = 0, r = n - 1;
while(l < r) {
int mid = l + r >> 1;
if(a[mid] >= k) r = mid;
else l = mid + 1;
}
if(a[l] != k) System.out.println("-1 -1");
else {
int left = l;//此时l = r 所以left = l 或者r 都可以 哪个都行
//System.out.println(l+" " ); 后面在输出l
l = 0;
r = n - 1;
while(l < r) {
int mid = l + r + 1 >> 1;
if(a[mid] <= k) l = mid;
else r = mid -1;
}
System.out.println(left + " " + l);
}
}
}
}
C++ 代码模板如下(来自y总的模板)
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
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;
}
浮点二分- 数的三次方根
这个简单就不用写那么多考虑条件
给定一个浮点数 n,求它的三次方根。
输入格式
共一行,包含一个浮点数 n。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留 6 位小数。
数据范围
−10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000
直接代码模板
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;
}
这里面的a,b要根据具体二分的范围来指定,对于eps的值,如果题目中要求保留4位小数,eps取1e-6,如果题目中要求保留6位小数,那eps取1e-8
import java.io.*;
class Main{
public static void main(String[]args)throws IOException{
BufferedReader input=new BufferedReader(new InputStreamReader(System.in));
double n=Double.parseDouble(input.readLine());
double l=-1000;
double r=1000;
while(r-l>1e-8){
double mid=(l+r)/2;
if(mid*mid*mid >= n) r=mid;//绿色区域
else l=mid;
}
System.out.printf("%.6f\n",l);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)