【数据结构与算法】二分查找算法

news2024/11/23 22:00:08

目录

  • 二分查找算法
    • 什么是二分查找算法
      • 整数二分法常用算法模板
    • 二分查找算法例题
      • 例题一:分巧克力问题
      • 例题二:M次方根

二分查找算法

什么是二分查找算法

  • 枚举查找顺序查找*,实现原理是逐个比较数组 a[0:n-1] 中的元素,直到找到元素 x 或搜索整个数组后确定 x 不在其中。最坏情况下需要比较N次,时间复杂度是 O(n),属于线性阶算法。*

  • 二分查找,也称为折半查找,是一种在有序数组或列表中查找特定元素的高效算法。它的基本思想是通过将查找范围逐渐缩小一半的方式来确定目标元素的位置。具体来说,它的步骤如下:

  1. 初始化:首先确定整个有序数组或列表作为初始的查找范围。
  2. 确定中间位置:计算初始查找范围的中间位置。
  3. 比较目标值:将目标值与中间位置的元素进行比较。
  4. 调整查找范围:根据比较结果,如果目标值等于中间位置的元素,则找到目标元素,算法结束;如果目标值小于中间位置的元素,则在左半部分继续查找;如果目标值大于中间位置的元素,则在右半部分继续查找。
  5. 重复步骤 2 到步骤 4,直到找到目标元素或查找范围为空。

二分查找算法的关键在于每一步将查找范围减半,因此它的时间复杂度为 O(log n),其中 n 为数组或列表的大小。这使得它在处理大规模数据时具有较高的效率。需要注意的是,二分查找要求数组或列表是有序的,否则无法保证正确性。

二分查找的示意图:
在这里插入图片描述

Step 1:

假设存在一个有序数组:
下标[ 0   1   2   3   4   5   6   7   8    9    10   11   12 ]
数据[ 7   14  18  21  23  29  31  35   38   42   46   49  52 ]
      ↑                                                   ↑
    low=0                                              high=12

                            mid=(low+high)/2
                            mid=(0+12)/2
                            mid=6
                            [mid]=31 > 14,所以选择左半部分

操作:
    此时令low不变,high=mid-1=5

Step 2:

下标[ 0   1   2   3   4   5   6   7   8    9    10   11   12 ]
数据[ 7   14  18  21  23  29  31  35   38   42   46   49  52 ]
      ↑                   ↑
   low=0                 high=5

            mid=(low+high)/2
            mid=(0+6)/2
            mid=3
            [mid]=21 > 14,所以选择左半部分

操作:
    此时令low不变,high=mid-1=2

Step 3:

下标[ 0   1   2   3   4   5   6   7   8    9    10   11   12 ]
数据[ 7   14  18  21  23  29  31  35   38   42   46   49  52 ]
      ↑       ↑
   low=0    high=2

            mid=(low+high)/2
            mid=(0+2)/2
            mid=1
            [mid]=14 = 14  找到答案

操作:
    返回下标

整数二分法常用算法模板

// 在单调递增序列a中查找>=x的数中最小的一个(即x或x的后继)
while (low < high)
{
    int mid = (low + high) / 2;
    if (a[mid] >= x)
        high = mid;

    else
        low = mid + 1;
}

// 在单调递增序列a中查找<=x的数中最大的一个(即x或x的前驱)
while (low < high)
{
    int mid = (low + high + 1) / 2;

    if (a[mid] <= x)
        low = mid;

    else
        high = mid - 1;
}

二分查找算法例题

例题一:分巧克力问题

在这里插入图片描述
输入示例:

2 10 6 5 5 6

输出示例:

2

解题思路:枚举所有的情况,从小到大排序进行切割,直到找到满足要求的巧克力边长。
求一块长方形 ( h ∗ w ) (h*w) (hw)最多被分成正方形 ( l e n ∗ l e n ) (len*len) (lenlen)巧克力个数为: n u m = ( h / l e n ) ∗ ( w / l e n ) num=(h/len)*(w/len) num=(h/len)(w/len)
为了减少运行时间,避免超时,采用二分查找的方法。
这个题解的解题思路非常清晰,我来为你总结一下:

解题思路

  1. 问题描述
    给定 n 块巧克力和 k 个小孩,每块巧克力的长和宽分别为h[i]w[i]。需要确定可以切割成 k 个相同大小的巧克力块的最大边长。

  2. 枚举所有情况

    • 题目要求找到满足要求的巧克力边长,这意味着需要枚举所有可能的边长,从小到大排序进行切割。
  3. 计算巧克力个数

    • 对于给定的长方形巧克力,将其切割成正方形(边长为 len)后,可以得到的正方形巧克力个数为 n u m = ( h [ i ] / l e n ) ∗ ( w [ i ] / l e n ) num = (h[i] / len) * (w[i] / len) num=(h[i]/len)(w[i]/len)
  4. 二分查找

    • 为了减少运行时间,避免超时,采用二分查找的方法。首先确定二分查找的上下界,其中下界至少为 1。然后,在二分查找的过程中,不断调整中间值(边长),直到找到满足要求的最大边长。
  5. 判断函数

    • 设计判断函数 pd(),用于确定给定边长是否能够满足条件,即是否可以切割成至少 k 个相同大小的巧克力块。

通过枚举、计算和二分查找的结合,能够高效地解决给定问题。在确保了正确性的同时,通过二分查找的方式,也使得算法的时间复杂度得到了有效地降低,提高了算法的效率。

#include<bits/stdc++.h>

using namespace std;
const int MAXN=100010;
int n,k;//巧克力的个数 小孩儿的个数
int h[MAXN],w[MAXN];

bool pd(int l)
{
    int sum=0;
    for(int i=0; i<n; i++)
    {
        sum+=(h[i]/l)*(w[i]/l);
        if(sum>=k)
        {
            return true;
        }
    }
    return false;
}

int main()
{
    cin>>n>>k;//输入 巧克力 和 小孩儿的个数
    for(int i=0; i<n; i++)
        cin>>h[i]>>w[i];//每块巧克力的长和宽

    //找到二分查找的上界
    int high=0;

    for(int i=0; i<n; i++)
    {
        high=max(high,h[i]);
        high=max(high,w[i]);
    }
    // 二分下届由题意可得至少为1
    int low=1;

    // 由于本题目就是求符合要求的Mid 值所以要将mid定义在二分查找外边
    int mid=0;
    while(low<high)
    {

        mid = (low + high+1) / 2;
        if(pd(mid))
            low=mid;
        else
            high = mid - 1;

//        cout<<low<<" "<<high<<endl;
    }

    //因为low=high所以输出哪一个都一样
    cout<<low;
    return 0;
}

例题二:M次方根

在这里插入图片描述
输入样例:

27 3

输出样例:

3.0000000

运行限制:

最大运行时间:1s
最大运行内存: 256M

注意:
1. 请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
2. 不要调用依赖于编译环境或操作系统的特殊函数。
3. 所有依赖的函数必须明确地在源文件中。
4. 不能通过工程设置而省略常用头文件。

题型通用模板:

//模版一:实数域二分,设置eps法

//令 eps 为小于题目精度一个数即可。比如题目说保留4位小数,0.0001 这种的。那么 eps 就可以设置为五位小数的任意一个数 0.00001- 0.00009 等等都可以。

//一般为了保证精度我们选取精度/100 的那个小数,即设置 eps= 0.0001/100 =1e-6

while (l + eps < r)
{
    double mid = (l + r) / 2;

    if (pd(mid))
        r = mid;
    else
        l = mid;
}

//模版二:实数域二分,规定循环次数法
//通过循环一定次数达到精度要求,这个一般 log2N < 精度即可。N 为循环次数,在不超过时间复杂度的情况下,可以选择给 N 乘一个系数使得精度更高。

    for (int i = 0; i < 100; i++)
{

    double mid = (l + r) / 2;
    if (pd(mid))
        r = mid;
    else
        l = mid;
}

判定条件pd():

double pd(double a,int m)
{
    double c=1;
    while(m>0)
    {//求a的m次方
        c=c*a;
        m--;
    }
    
    if(c>=n)//如果C大于等于原来的数 n
        return true;
    else
        return false;
}

解题分析:

  1. 问题理解

    • 该程序解决了一个求解数的 M 次方根的问题,其中给定了一个数 n 和一个整数m,需要求出 n m 次方根。
  2. 输入与初始化

    • 程序首先通过输入流 cin 读取了两个变量 n m
    • 初始化了两个变量 l r,分别表示二分查找的左右边界,初始值分别为0 n
  3. 二分查找

    • 采用了实数域二分的方式进行查找,即在区间 [l, r] 上进行二分查找。
    • while 循环中,通过计算中间值 mid,并调用函数 pd(mid, m) 判断 mid m 次方是否大于等于 n,来决定下一步搜索的方向。
    • 不断调整边界 l r,直到满足精度要求(eps)
  4. 判断函数 pd

    • 函数pd用于判断给定的数am 次方是否大于等于 n。如果是,则返回 true,否则返回 false。
  5. 输出结果

    • 输出结果时,采用了 cout 进行输出,并通过 setprecision(7) 设置了输出的精度为 7 位小数。

算法复杂度分析

  • 时间复杂度:由于采用了实数域二分查找,算法的时间复杂度为 O(log n),其中 n 为目标数。
  • 空间复杂度:算法使用了常数级别的额外空间,空间复杂度为 O(1)

题解代码:

#include <cstdio>
#include <iostream>
#include<iomanip> //用于浮点数输出
using namespace std;

double n,l,r,mid;
double eps=1e-8;

bool pd(double a,int m)
{
    double c=1;
    while(m>0)
    {
        c=c*a;
        m--;
    }
    if(c>=n)
        return true;
    else
        return false;
}

int main()
{
    int m;
    cin>>n>>m;
//设置二分边界
    l=0,r=n;
//实数二分
    while (l + eps < r)
    {
        double mid = (l + r) / 2;
        if (pd(mid,m))
            r = mid;
        else
            l = mid;
    }

    cout<<fixed<<setprecision(7)<<l;
    //一般使用print
    //printf("%x.yf",n)
    //其中X是固定整数长度,小数点前的整数位数不够,会在前面补0
    //y是保留小数位数,不够补零
    //printf("%.7f",l);
    return 0;
}

感谢阅读!!!
【数据结构与算法】系列文章链接: 【数据结构与算法】递推法和递归法解题(递归递推算法典型例题)
【数据结构与算法】系列文章链接: 【数据结构与算法】C++的STL模板(迭代器iterator、容器vector、队列queue、集合set、映射map)以及算法例题
【数据结构与算法】系列文章链接: 【数据结构与算法】搜索算法(深度优先搜索 DFS和广度优先搜索 BFS)以及典型算法例题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1589143.html

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

相关文章

NFT Insider #126:Azuki 创始人将探索使用 AnimeChain 向 NFT 所有者分配版税

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members &#xff08;https://twitter.com/WHALEMembers&#xff09;、BeepCrypto &#xff08;https://twitter.com/beep_crypto&#xff09;联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜…

互联网轻量级框架整合之设计模式

反射技术 Java的反射技术能够通过配置类的全限定名、方法和参数完成对象的初始化&#xff0c;甚至反射某些方法&#xff0c;大大的增强了Java的可配置型&#xff0c;这也是Spring IoC的底层原理&#xff0c;Java的反射技术覆盖面很广&#xff0c;包括对象构建、反射方法、注解、…

【Linux杂货铺】文件系统

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 硬盘 &#x1f4c2; 物理结构 &#x1f4c2; 存储结构 &#x1f4c2; CHS定址法 &#x1f4c2; 操作系统对硬盘的管理和抽象 &#x1f4c1; 文件系统 &#x1f4c2; 分区 &#x1f4c2; 分组 &#x1f4c2; inode号 分配…

十分钟到底能不能讲明白ROS到底能做啥

总结 录完视频发现十分钟不能&#xff0c;总共花了20分钟。 提纲&#xff1a; 课程、竞赛、论文Linux、C、Python、Github和ROS关联性强平台-资格和ROS关联性弱速度-成绩路径规划-全局和局部全局-侧重路径长短-找一条最优&#xff08;短&#xff09;的路局部-侧重速度控制-用…

LeetCode-72. 编辑距离【字符串 动态规划】

LeetCode-72. 编辑距离【字符串 动态规划】 题目描述&#xff1a;解题思路一&#xff1a;动规五部曲解题思路二&#xff1a;动态规划【版本二】解题思路三&#xff1a;0 题目描述&#xff1a; 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最…

【R语言从0到精通】-3-R统计分析(列联表、独立性检验、相关性检验、t检验)

上两次教程集中学习了R语言的基本知识&#xff0c;那么我们很多时候使用R语言是进行统计分析&#xff0c;因此对于生物信息学和统计科学来说&#xff0c;R语言提供了简单优雅的方式进行统计分析。教程参考《Rlearning》 3.1 描述性统计分析 3.1.1 载入数据集及summary函数 我…

安卓一键logo设计工具_V3.6.9.1 高级版

【分析】&#xff1a;LOGO设计软件&#xff0c;可以一键生成无版权的网站LOGO等等。 网盘自动获取 链接&#xff1a;https://pan.baidu.com/s/1lpzKPim76qettahxvxtjaQ?pwd0b8x 提取码&#xff1a;0b8x

看linux内核启动流程需要的arm汇编学习笔记(二)

文章目录 一、ldr1.地址偏移模式2.变基模式3.标签3.1 访问宏定义3.2 访问一个字符串3.3 访问一个data 二、ldp和stp1.双字节加载2.双字节存储3.双字节存储的后变基模式 三、位操作1. 移位2. 按位操作3. 位段插入4.位段提取5.零计数指令 四、跳转指令1. cmp比较两个数2. cmn负向…

redis怪谈

缓存穿透、击穿、雪崩 《缓存三兄弟》 穿透无中生有key&#xff0c;布隆过滤null隔离 缓存击穿过期key&#xff0c;锁与非期解难题 雪崩大量过期key&#xff0c;过期时间要随机 面试必考三 兄 弟&#xff0c;可用限流来保底 什么是缓存穿透 指查询一个一定不存在的数据&#x…

CRMEB多商户商城系统,不止B2B2C

CRMEB多商户商城系统&#xff0c;是将多个商家汇聚到一个平台上开店&#xff0c;就像常见的京东、淘宝等。这个平台上一般包含4种不同角色&#xff0c;即平台运营管理方、入驻商家、供应商、消费者。 因为平台角色的多元化&#xff0c;多商户商城系统也具有联营、自营、招商、…

事务,MySQL函数和索引详解

文章目录 事务简介提交方式手动提交事务 事务执行流程修改事务的默认提交方式 事务原理四大特性隔离级别 MySQL函数常见的日期函数判断函数case when字符串函数数字函数 MySQL性能(了解)索引概念分类MySQL索引语法数据结构(了解)BTreeBTree好处 优缺点优势劣势 创建原则 事务简…

echarts折线图自定义打点标记小工具

由于没研究明白echarts怎么用label和lableLine实现自定义打点标记&#xff0c;索性用markPoint把长方形压扁成线模拟了一番自定义打点标记&#xff0c;记录下来备用。&#xff08;markLine同理也能实现&#xff09; 实现代码如下&#xff1a; <!DOCTYPE html> <html…

【python】在pycharm用Django写一个API接口

背景 Django是一个高级的Python Web框架&#xff0c;它鼓励快速开发和干净、实用的设计。它由经验丰富的开发者设计&#xff0c;解决了Web开发的大部分麻烦&#xff0c;因此开发者可以专注于编写应用而不是重复造轮子。Django遵循MVC设计模式&#xff0c;并拥有自带的一套便捷…

「世界看两会」南非开普敦大学教授:中非之间的信任是宝贵资产

南非开普敦大学的卡洛斯洛佩斯教授在中国日报发表的文章中强调了中非之间所建立起的信任关系的重要性&#xff0c;视其为促进双方深化合作与互利共赢的关键性资产。他认为&#xff0c;中国两会是中国和非洲国家加强合作关系、规划共同发展战略的重要时机。 洛佩斯教授指出&…

内存地产风云录:malloc、free、calloc、realloc演绎动态内存世界的楼盘开发与交易大戏

欢迎来到白刘的领域 Miracle_86.-CSDN博客 系列专栏 C语言知识 先赞后看&#xff0c;已成习惯 创作不易&#xff0c;多多支持&#xff01; 在这个波澜壮阔的内存地产世界中&#xff0c;malloc、free、calloc和realloc四位主角&#xff0c;共同演绎着一场场精彩绝伦的楼盘开…

数学知识——欧几里得算法(辗转相除法)

欧几里得算法用来求最大公约数 int gcd(int a, int b) {if(b 0) return a;else return gcd(b, a % b); } 例题&#xff1a;洛谷p1029 #include<iostream>using namespace std;#define int long long #define endl \nint x, y; int ans;int gcd(int x, int y) {if(y 0)…

C语言世界上最详细自定义类型:联合和枚举

前言&#xff1a; hello! 大家好&#xff0c;我是小陈&#xff0c;今天给大家带来一篇联合和枚举的博客&#xff01;&#xff01;&#xff01; 1.联合体类型的声明 像结构体⼀样&#xff0c;联合体也是由⼀个或者多个成员构成&#xff0c;这些成员可以不同的类型。 但是编译…

vue 文件预览

<template><div><p>打开新页面预览文件</p><div v-for"(item,index) in list" :key"index"><el-link type"primary" click"handleOpen(item.url)">{{item.name}}</el-link></div><…

Day 24 回溯理论基础 77. 组合

回溯理论基础 ​ 在递归中已经提到过了&#xff0c;回溯是递归的副产品&#xff0c;只要有递归就会有回溯&#xff1b; ​ 回溯法本质是穷举&#xff0c;穷举所有可能&#xff0c;然后选出需要的答案&#xff0c;并不是什么高效的算法&#xff1b; ​ 不高效但又不得不用&am…

Python3.7编程之病毒

基础篇 什么是病毒 病毒&#xff0c;指的是一些通过非法手段获取系统的一些权限&#xff0c;然后进行破坏或者盗取。 病毒分为两类&#xff1a; 1、破坏型 这类病毒往往会将系统弄的乱七八糟&#xff0c;比如把你的U盘删光&#xff0c;把你的系统背景调成黑客图片&#xff0c…