整数二分算法和浮点数二分算法

devtools/2024/10/18 23:29:08/

整数二分算法和浮点数二分算法


二分

现实中运用到二分的就是猜数字的游戏 假如有A同学说B同学所说数的大小,B同学要在1~100中间猜中数字65,当B同学每次说的数都是范围的一半时这就算是一个二分查找的过程
在这里插入图片描述

二分查找的前提是这个数字序列要有单调性

基本步骤

初始化:

设定两个指针,left 和 right,分别指向数组的起始和末尾。

计算中间位置:

使用公式 mid = left + (right - left) / 2 或者left+right>>1计算中间位置。

比较:

如果中间位置的元素等于目标值,返回中间位置。
如果目标值小于中间位置的元素,则将 right 更新为 mid - 1,继续在左半部分查找。
如果目标值大于中间位置的元素,则将 left 更新为 mid + 1,继续在右半部分查找。

重复:

重复步骤 2 和 3,直到 left 超过 right。

结束:

如果在查找过程中未找到目标值,返回一个表示未找到的结果(如 -1 或 None)。

二分查找算法的时间复杂度是 O(log n),非常高效。

整数二分

二分的本质并不是一定要单调,而是对一个区间可以化分成两个部分,一部分一定满足条件,另一部分一定不满足,对于满足这种条件的我们可以找出两个边界点,这样的话二分算法可以寻找这个性质的边界(红色和黑色的边界都行,因为是整数二分所以两边界不重合)。
在这里插入图片描述

第一种情况(二分左半部分)

假如说有一串数字1,2,3,3,4,4,5,6,8,8我们需要找到满足小于等于3的最大情况的子序列,也就是我们需要找到最后一次出现的3,我们可以如何做呢?
我们让一个mid=(l+r+1)/2 假如说a[mid]<=3,此时if(check(mid))==true说明此时mid指向的值可能是答案,但是我们无法保证其后面还有没有答案是<=3的,所以此时应该是l=mid,假如说此时a[mid]>=3,if(check(mid))==false说明此时mid指向的是一定不满足条件的的那么此时应该是r=mid-1
在这里插入图片描述
我们继续不段重复以上操作直到l>=r时退出循环。

第二种情况(二分右半部分)

还是这个数字序列这次我们要找1,2,3,3,4,4,5,6,8,8中第一次出现3的位置,我们应该怎么做呢?
我们还是让一个mid=(l+r)/2 假如说a[mid]>=3,此时if(check(mid))==true说明此时mid指向的值可能是答案,但是我们无法保证其前面还有没有答案是>=3的,所以此时应该是r=mid,假如说此时a[mid]<=3,if(check(mid))==false说明此时mid指向的是一定不满足条件的的那么此时应该是l=mid+1

注意:第一种情况是(l+r+1)而不是(l+r)为为什么呢?

因为计算机的除法都是向下取整的所以就会出现问题,假如说此时l=r-1那么mid=(l+r)/2=(l+l+1)/2=l然后我们假如发现l还是满足条件的,那么此时就会陷入l=mid,mid=l的死循环

我们来写一道题

洛谷P2249
下面的代码是既有第一次出现,也有最后一次出现的,两种情况都写了。
代码如下:

#include <iostream>
using namespace std;const int N = 1e5 + 10; // 定义数组的最大容量,数组a最多可以存放1e5个元素
int a[N], n, m; // 定义全局变量数组a,n为数组长度,m为查询次数// check1函数用于检查a[mid]是否大于等于目标值x
bool check1(int mid, int x) {if (a[mid] >= x) {return true; // 如果a[mid]大于等于x,返回true,表示满足条件} else {return false; // 否则返回false,表示不满足条件}
}// check2函数用于检查a[mid]是否小于等于目标值x
bool check2(int mid, int x) {if (a[mid] <= x) {return true; // 如果a[mid]小于等于x,返回true,表示满足条件} else {return false; // 否则返回false,表示不满足条件}
}int main() {cin >> n >> m; // 输入数组的长度n和查询的次数mfor (int i = 1; i <= n; i++) {cin >> a[i]; // 依次输入数组a的元素}while (m--) { // 对每一次查询进行处理,m次查询int x; // 定义要查询的目标值xcin >> x; // 输入目标值x// 首先进行二分查找,寻找第一个大于等于x的位置int l = 1, r = n; // 初始化左右边界,l是左边界,r是右边界while (l < r) { // 当左边界小于右边界时,继续二分查找int mid = (l + r) >> 1; // 计算中间位置midif (check1(mid, x)) { // 如果a[mid]大于等于xr = mid; // 缩小右边界至mid} else {l = mid + 1; // 否则缩小左边界至mid+1}}// 查找完成后,检查a[l]是否等于目标值xif (a[l] == x) {cout << l << " "; // 如果a[l]等于x,输出位置l} else {cout << -1 << " "; // 如果a[l]不等于x,输出-1表示未找到}// 再进行一次二分查找,寻找最后一个小于等于x的位置l = 1, r = n; // 重新初始化左右边界while (l < r) {int mid = (l + r + 1) >> 1; // 计算中间位置mid,向上取整if (check2(mid, x)) { // 如果a[mid]小于等于xl = mid; // 缩小左边界至mid} else {r = mid - 1; // 否则缩小右边界至mid-1}}// 查找完成后,再次检查a[l]是否等于目标值xif (a[l] == x) {cout << l << " "; // 如果a[l]等于x,输出找到的最后位置l} else {cout << -1 << " "; // 如果没找到,输出-1}cout<<endl;}
}

整数二分模板

bool check(int x) {// 这里是判断x是否满足某种性质的函数,具体实现取决于实际问题// 可以根据x的值来返回true或false,用于二分查找中的判断/* ... */
}// 区间[l, r]被划分为[l, mid]和[mid + 1, r]时使用的二分查找
int bsearch_1(int l, int r) {// 二分查找的目的是在区间[l, r]中寻找满足某种性质的最小位置// l 是左边界,r 是右边界,最终返回满足性质的最小下标while (l < r) { // 当左边界小于右边界时,继续进行二分查找int mid = (l + r) >> 1; // 计算中间位置mid,使用右移操作进行快速计算,相当于 (l + r) / 2if (check(mid)) { // 如果check(mid)为true,表示mid满足性质r = mid; // 将右边界缩小到mid,因为我们要找满足性质的最小位置} else { // 否则,mid不满足性质l = mid + 1; // 将左边界缩小到mid + 1,因为mid以及它左边的值都不满足条件}}return l; // 返回最终的左边界,此时l == r,且为满足性质的最小位置
}// 区间[l, r]被划分为[l, mid - 1]和[mid, r]时使用的二分查找
int bsearch_2(int l, int r) {// 二分查找的目的是在区间[l, r]中寻找满足某种性质的最大位置// l 是左边界,r 是右边界,最终返回满足性质的最大下标while (l < r) { // 当左边界小于右边界时,继续进行二分查找int mid = (l + r + 1) >> 1; // 计算中间位置mid,并向上取整,确保mid偏向右侧if (check(mid)) { // 如果check(mid)为true,表示mid满足性质l = mid; // 将左边界缩小到mid,因为我们要找满足性质的最大位置} else { // 否则,mid不满足性质r = mid - 1; // 将右边界缩小到mid - 1,因为mid以及它右边的值都不满足条件}}return l; // 返回最终的左边界,此时l == r,且为满足性质的最大位置
}

浮点数二分算法

浮点数二分相较于整数二分更加简单因为只有一个模板,并且没有边界问题,浮点数的二分查找可以用于求解需要精确值的问题,例如求方程的解或几何问题中涉及浮点精度的求解。与整数二分查找不同,浮点数二分查找必须考虑精度问题,因为浮点数无法精确到某个具体值,所以我们需要一个精度(如 epsilon),用于判断二分查找的终止条件。

假如说我们需要找一个数x的平方等于目标值2

代码如下:

#include <iostream>
#include <cmath> // 包含abs函数,用于计算绝对值
using namespace std;// 定义一个需要使用二分法求解的函数,返回值为目标函数值
double f(double x) {// 举例:寻找函数 f(x) = x^2 - 2 的根return x * x - 2;
}int main() {double l = 0, r = 2; // 初始区间[l, r],假设根位于[0, 2]之间double eps = 1e-7;   // 定义精度eps,即当结果误差小于1e-7时停止迭代// 当区间宽度大于精度要求时,继续二分while (r - l > eps) {double mid = (l + r) / 2; // 计算中间值if (f(mid) > 0) { // 如果 f(mid) > 0,表示 mid 处的值在根的右侧r = mid; // 缩小右边界至mid} else { // 否则 f(mid) <= 0,表示 mid 处的值在根的左侧或是根l = mid; // 缩小左边界至mid}}// 输出结果,l 和 r 最终都会逼近根cout << "x ≈ " << l << endl;cout << "验证结果: f(x) = " << f(l) << endl; // 输出验证f(l)接近0
}

浮点数二分模板

// 函数原型:在浮点数区间 [l, r] 上使用二分查找,找到满足某种性质的浮点数
double bsearch_3(double l, double r)
{const double eps = 1e-6;   // eps 是精度控制的参数,当区间长度小于 eps 时停止二分查找// 1e-6 表示小数点后 6 位精度,可根据题目要求调整精度// 当区间长度大于 eps 时,继续进行二分查找while (r - l > eps){// 计算区间的中点 middouble mid = (l + r) / 2;// 调用 check 函数判断 mid 是否满足给定的性质// 假设 check(mid) 返回 true,则意味着 mid 及其右侧可能满足性质,// 因此将右区间收缩到 mid,继续在左侧区间 [l, mid] 上搜索if (check(mid)) r = mid;// 否则,mid 及其左侧不满足性质,因此我们将左区间收缩到 mid,继续在右侧区间 [mid, r] 上搜索else l = mid;}// 最后返回左边界 l(或右边界 r),此时区间已经很小,接近于精度要求的结果// 因为 l 和 r 的距离非常小,最终答案应为 l 或 r 的近似值return l;
}

整数二分算法和浮点数二分算法源代码


http://www.ppmy.cn/devtools/115262.html

相关文章

Python语言基础教程(下)4.0

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…

力扣-96.不同的二叉搜索树 题目详解

题目: 给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 二叉搜索树介绍: 二叉搜索树是一个有序树&#xff1a; 若它的左子树不空&#xff0c;则左子树上所有结点的值均小于它…

SpringBoot使用@Scheduled注解,实现多线程定时任务处理

1.在定时任务类上加上注解 EnableScheduling&#xff0c;开启定时任务管理&#xff0c; 使用PostConstruct 注解&#xff0c;进行初始化操作&#xff0c;并设置多任务线程池&#xff1a; Slf4j RequiredArgsConstructor EnableScheduling Component public class ScheduleExec…

springboot+redis+缓存

整合 添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 连接redis&#xff0c;配置yml文件 主机 端口号 数据库是哪一个 密码 配置类 p…

边缘计算智能网关的功能应用与优势-天拓四方

在物联网的世界中&#xff0c;数以亿计的设备不断产生、传输和处理数据。然而&#xff0c;传统的云计算架构在面对这些实时性要求高、数据量庞大的物联网数据时&#xff0c;常常面临着网络延迟、带宽限制和安全风险等问题。这时&#xff0c;边缘计算智能网关作为一种新兴的技术…

C++校招面经(二)

欢迎关注 0voice GitHub 6、 C 和 Java 区别&#xff08;语⾔特性&#xff0c;垃圾回收&#xff0c;应⽤场景等&#xff09; 指针&#xff1a; Java 语⾔让程序员没法找到指针来直接访问内存&#xff0c;没有指针的概念&#xff0c;并有内存的⾃动管理功能&#xff0c;从⽽…

ubuntu 22.04 ~24.04 如何修改登录背景

ubuntu 22.04 ~24.04 如何修改登录背景 背景&#xff1a;由于22.04 登录gdm的变更&#xff0c;之前的修改登录背景的方案已经无法使用。现在给大家分享新的使用方法&#xff1a; 1&#xff0c;下载如下路径的脚本&#xff1a; https://download.csdn.net/download/xdhyqd/89…

2024年华为杯数学建模研赛(F题) 建模解析| 卫星轨道 | 小鹿学长带队指引全代码文章与思路

我是鹿鹿学长&#xff0c;就读于上海交通大学&#xff0c;截至目前已经帮2000人完成了建模与思路的构建的处理了&#xff5e; 本篇文章是鹿鹿学长经过深度思考&#xff0c;独辟蹊径&#xff0c;实现综合建模。独创复杂系统视角&#xff0c;帮助你解决研赛的难关呀。 完整内容可…