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

news2025/1/1 8:42:57

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


二分

现实中运用到二分的就是猜数字的游戏 假如有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和查询的次数m
    for (int i = 1; i <= n; i++) {
        cin >> a[i]; // 依次输入数组a的元素
    }

    while (m--) { // 对每一次查询进行处理,m次查询
        int x; // 定义要查询的目标值x
        cin >> x; // 输入目标值x
        
        // 首先进行二分查找,寻找第一个大于等于x的位置
        int l = 1, r = n; // 初始化左右边界,l是左边界,r是右边界
        while (l < r) { // 当左边界小于右边界时,继续二分查找
            int mid = (l + r) >> 1; // 计算中间位置mid
            if (check1(mid, x)) { // 如果a[mid]大于等于x
                r = mid; // 缩小右边界至mid
            } else {
                l = mid + 1; // 否则缩小左边界至mid+1
            }
        }

        // 查找完成后,检查a[l]是否等于目标值x
        if (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]小于等于x
                l = mid; // 缩小左边界至mid
            } else {
                r = mid - 1; // 否则缩小右边界至mid-1
            }
        }

        // 查找完成后,再次检查a[l]是否等于目标值x
        if (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) / 2
        if (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)
    {
        // 计算区间的中点 mid
        double 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.coloradmin.cn/o/2143503.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅谈树形结构——特殊的树——二叉树

文章目录 一、什么是二叉树&#xff1f;二、二叉树的特点三、二叉树的性质四、两种特殊的二叉树4.1、满二叉树4.2、完全二叉树 五、一些关于二叉树特性的习题六、二叉树的存储[代码实现]6.1、链式存储6.2、顺序存储 七、二叉树的遍历7.1、关于二叉树遍历的选择题练习 一、什么是…

Linux系统终端中文件权限的10位字符是什么意思

Linux操作系统终端长格式显示的文件 在Linux操作系统终端中用文件长格式命令ls -l显示文件&#xff0c;如上图。第一列10个字符表示的含义如下&#xff1a; drwxrwxrwx 第一个字符是表示该文件的类型&#xff0c;如红色d表示该文件是一个目录&#xff0c;详细内容可以参考我…

ROS 编程入门的介绍

2.1 创建 ROS 功能包 ROS&#xff08;Robot Operating System&#xff09;是一种开源的机器人软件框架&#xff0c;广泛用于机器人开发中。通过使用 ROS&#xff0c;开发者可以轻松创建和管理机器人应用程序。在本节中&#xff0c;我们将介绍如何创建一个 ROS 功能包并实现一些…

高德地图2.0 绘制、编辑多边形覆盖物(电子围栏)

1. 安装 npm i amap/amap-jsapi-loader --save移步&#xff1a;官方文档 2. map组件封装 <script lang"ts" setup> import AMapLoader from amap/amap-jsapi-loader import { onMounted, ref } from vue import { propTypes } from /utils/propTypesdefineO…

【自动驾驶】决策规划算法 | 数学基础(三)直角坐标与自然坐标转换Ⅱ

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

以root用户登陆ubuntu的桌面环境

前言 在学习Linux的时候&#xff0c;经常都需要使用sudo权限来对配置文件进行修改&#xff0c;常用的方法就是用vim编辑器在命令行界面进行修改&#xff0c;比如sudo vim /etc/profile&#xff0c;但我觉得每次都用命令行挺麻烦的&#xff0c;于是&#xff01;&#x1f913;我…

【零散技术】Odoo17通过Controller下载PDF

序言:时间是我们最宝贵的财富,珍惜手上的每个时分 Odoo作为一款开源ERP&#xff0c;拥有极佳的拓展性&#xff0c;Odoo的Controller框架也让它具备了作为微信小程序后端的能力&#xff0c;那么就存在 需要通过小程序来下载PDF的业务情况。 目录 1.功能代码 1.1 manifest 设置 …

neo4j 图数据库使用教程

文章目录 neo4j 图数据库使用教程1&#xff09; 下载2&#xff09;安装3&#xff09; 创建数据4&#xff09;查询数据5&#xff09; 更新数据6&#xff09; 建立索引 neo4j 图数据库使用教程 1&#xff09; 下载 neo4j下载地址 http://dist.neo4j.org/neo4j-community-3.5.2…

【C++前后缀分解】1888. 使二进制字符串字符交替的最少反转次数|2005

本文涉及知识点 C前后缀分解 LeetCode1888. 使二进制字符串字符交替的最少反转次数 给你一个二进制字符串 s 。你可以按任意顺序执行以下两种操作任意次&#xff1a; 类型 1 &#xff1a;删除 字符串 s 的第一个字符并将它 添加 到字符串结尾。 类型 2 &#xff1a;选择 字符…

redis群集三种模式:主从复制、哨兵、集群

redis群集有三种模式 redis群集有三种模式&#xff0c;分别是主从同步/复制、哨兵模式、Cluster&#xff0c;下面会讲解一下三种模式的工作方式&#xff0c;以及如何搭建cluster群集 ●主从复制&#xff1a;主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制…

合宙Air201模组LuatOS扩展功能:温湿度传感器篇!

通过前面几期的学习&#xff0c;同学们的学习热情越来越高。 合宙Air201模组除了支持3种定位方式外&#xff0c;还具有丰富的扩展功能&#xff0c;比如&#xff1a;通过外扩BTB链接方案&#xff0c;最多可支持21个IO接口&#xff1a;SPI、I2C、UART等多种接口全部支持。 本期…

【时时三省】(C语言基础)指针进阶 例题8

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 第一个打印2 a6不管它是多大 前面是&#xff1d;s 都得变成两个字节 所以打印2 第二个打印5 sizeof里面的表达式是不参与运算的 所以打印5 上面所有例题总结…

从边缘设备到云端平台,合宙DTURTU打造无缝物联网解决方案

如今&#xff0c;物联网&#xff08;IoT&#xff09;技术飞速发展&#xff0c;万物互联的时代已然到来&#xff0c;那么&#xff0c;高效、稳定地连接边缘设备与云端平台&#xff0c;实现数据的实时采集、传输与处理&#xff0c;就成为了推动物联网应用落地的关键。 DTU&#…

硬件工程师笔试面试——无线通讯模块

目录 15、无线通讯模块 15.1 基础 无线通讯模块实物图 15.1.1 概念 15.1.2 常见的无线通讯模块及其特点 15.1.3 无线通讯模块参数 15.1.4 无线通讯模块工作原理 15.2 相关问题 15.2.1 如何根据项目需求选择合适的无线通讯模块? 15.2.2 无线通讯模块的安全性如何,如…

合宙Air201模组LuatOS:点点鼠标就搞定的FOTA远程升级,你知道吗?

你是不是也经常遇到小伙伴吐槽&#xff1a;开发是个苦差事&#xff01;做项目倒还好&#xff0c;就怕遇到项目升级&#xff0c;那简直让人头大。。。 如果你也有这种困惑&#xff0c;就多了解一下合宙的开发工具&#xff0c;简单实用又高效&#xff0c;甚至只需点点鼠标&#…

【变化检测】基于ChangeStar建筑物(LEVIR-CD)变化检测实战及ONNX推理

主要内容如下&#xff1a; 1、LEVIR-CD数据集介绍及下载 2、运行环境安装 3、ChangeStar模型训练与预测 4、Onnx运行及可视化 运行环境&#xff1a;Python3.8&#xff0c;torch1.12.0cu113&#xff0c;onnxruntime-gpu1.12.0 likyoo变化检测源码&#xff1a;https://github.c…

【楚怡杯】职业院校技能大赛 “云计算应用” 赛项样题四

某企业根据自身业务需求&#xff0c;实施数字化转型&#xff0c;规划和建设数字化平台&#xff0c;平台聚焦“DevOps开发运维一体化”和“数据驱动产品开发”&#xff0c;拟采用开源OpenStack搭建企业内部私有云平台&#xff0c;开源Kubernetes搭建云原生服务平台&#xff0c;选…

动手学深度学习(四)卷积神经网络-下

全连接层存在的问题&#xff1a;参数过大&#xff0c;计算成本过高。 一、网络中的网络&#xff08;NiN&#xff09; 1、NiN块 ①NiN块的结构 NiN串联多个由卷积层和“全连接”层构成的小网络来构建一个深层网络。这种由卷积层和“全连接”层构成的小网络就是NiN块。 &#…

线程池夺命十四问

目录 一&#xff1a;什么是线程池 二&#xff1a;线程池有什么好处 三&#xff1a;如何创建一个线程池 Executors ThreadPoolExecutors 四&#xff1a;创建一个线程池为什么不推荐使用Executors 五&#xff1a;如何设置线程池的大小 六&#xff1a;线程池有哪些参数 …

(CS231n课程笔记)深度学习之损失函数详解(SVM loss,Softmax,熵,交叉熵,KL散度)

学完了线性分类&#xff0c;我们要开始对预测结果进行评估&#xff0c;进而优化权重w&#xff0c;提高预测精度&#xff0c;这就要用到损失函数。 损失函数&#xff08;Loss Function&#xff09;是机器学习模型中的一个关键概念&#xff0c;用于衡量模型的预测结果与真实标签…