CSP-J 算法基础 二分查找与二分答案

news/2024/11/13 6:44:04/
cle class="baidu_pl">
cle_content" class="article_content clearfix">
content_views" class="markdown_views prism-atom-one-dark">cap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">

class="toc">

文章目录

  • 前言
  • 二分思想
    • 二分思想的工作原理
    • 二分思想的好处
    • 二分思想的应用场景
  • 编程实现二分class="tags" href="/SuanFa.html" title=算法>算法
    • 二分查找class="tags" href="/SuanFa.html" title=算法>算法的思考过程
      • 步骤一:初始化查找范围
      • 步骤二:缩小范围
      • 步骤三:再次缩小范围
      • 二分查找过程中的索引变化:
    • C语言实现二分查找
      • 代码分析
      • class="tags" href="/SuanFa.html" title=算法>算法复杂度
  • 二分答案
    • 二分答案的过程:
    • 适用场景:
  • 编程实现二分答案
      • C代码示例:
      • 输出示例:
      • 代码说明:
  • 总结


前言

class="tags" href="/SuanFa.html" title=算法>算法的世界里࿰c;二分查找是一种经典的、非常高效的搜索方法࿰c;通常用于在有序数组中快速定位某个目标值。而“二分答案”则更进一步࿰c;将二分思想运用于更广泛的问题领域࿰c;通过不断缩小搜索范围࿰c;逐步逼近一个最优解或找到某个满足特定条件的临界值。

二分查找与二分答案作为class="tags" href="/SuanFa.html" title=算法>算法基础的核心思想࿰c;在CSP-J考试中频繁出现࿰c;掌握这两种方法不仅能够帮助我们解决查找问题࿰c;还能有效应对最小化、最大化及临界点等复杂问题。


二分思想

二分思想(也称为二分法或二分查找)是一种常用的class="tags" href="/SuanFa.html" title=算法>算法思想࿰c;特别适用于在有序数组或问题空间中查找特定元素或解的场景。核心思想是通过每次将问题的范围对半缩小࿰c;从而迅速逼近目标值。这种思想能够大大减少查找的次数࿰c;提升效率。

二分思想的工作原理

  1. 初始状态:假设有一个已经排序的数组或问题空间࿰c;我们要在其中查找某个目标元素或特定的解。
  2. 中间点选择:计算当前区间的中间位置࿰c;并将这个中间值与目标值进行比较。
  3. 范围缩小
    • 如果中间值等于目标值࿰c;查找结束。
    • 如果中间值大于目标值࿰c;那么目标值一定在中间值左侧࿰c;因此将搜索范围缩小到左半部分。
    • 如果中间值小于目标值࿰c;则将搜索范围缩小到右半部分。
  4. 重复上述过程c;每次将查找范围对半缩小࿰c;直到找到目标值或查找范围为空。

二分思想的好处

  1. 高效性:二分查找的时间复杂度为 O(log n)࿰c;远优于 O(n) 的线性查找。特别是在大规模数据集或搜索空间中࿰c;二分法能够显著加快查找速度。
  2. 简洁性class="tags" href="/SuanFa.html" title=算法>算法逻辑简单清晰࿰c;易于理解和实现。
  3. 应用广泛:二分思想不仅可以用于数组查找࿰c;还可以用于求解数学问题、优化问题(如在函数的单调区间内查找极值)、搜索阈值等各种场景。

二分思想的应用场景

  • 有序数组查找:在一个已排序的数组中查找目标元素。
  • 问题求解:例如在一定范围内查找某个满足条件的数值(如平方根的近似值、最优解的逼近等)。
  • 搜索阈值:确定一个系统能够承受的最大负荷、最小资源需求等。

总的来说࿰c;二分思想的核心优势在于它能够大大减少搜索的次数࿰c;使得在大规模问题中表现尤为优异。

编程实现二分class="tags" href="/SuanFa.html" title=算法>算法

二分查找class="tags" href="/SuanFa.html" title=算法>算法的思考过程

假设我们有一个有序的数组 <code>arr = [1, 3, 7, 9, 12, 15, 18, 20, 23, 27]code>࿰c;我们想要找到数字 <code>15code>。

步骤一:初始化查找范围

  • 初始范围是整个数组࿰c;即索引 <code>0code> 到 <code>9code>࿰c;中间索引为 <code>(0 + 9) / 2 = 4code>(向下取整)。
  • 比较 <code>arr[4] = 12code> 和 <code>15code>࿰c;因为 <code>12 < 15code>࿰c;所以目标一定在右半部分。

步骤二:缩小范围

  • 新的查找范围是索引 <code>5code> 到 <code>9code>࿰c;新的中间索引为 <code>(5 + 9) / 2 = 7code>。
  • 比较 <code>arr[7] = 20code> 和 <code>15code>࿰c;因为 <code>20 > 15code>࿰c;所以目标在左半部分。

步骤三:再次缩小范围

  • 新的查找范围是索引 <code>5code> 到 <code>6code>࿰c;新的中间索引为 <code>(5 + 6) / 2 = 5code>。
  • 比较 <code>arr[5] = 15code> 和 <code>15code>࿰c;找到了目标࿰c;class="tags" href="/SuanFa.html" title=算法>算法结束。

二分查找过程中的索引变化:

  1. 初始查找范围 <code>[0, 9]code>࿰c;中间索引 <code>4code>࿰c;比较结果 <code>arr[4] = 12code>。
  2. 缩小范围 <code>[5, 9]code>࿰c;中间索引 <code>7code>࿰c;比较结果 <code>arr[7] = 20code>。
  3. 缩小范围 <code>[5, 6]code>࿰c;中间索引 <code>5code>࿰c;找到 <code>arr[5] = 15code>。

C语言实现二分查找

<code class="prism language-c">class="token macro property">class="token directive-hash">#class="token directive keyword">include class="token string"><stdio.h>class="token comment">// 二分查找函数࿰c;返回目标元素的索引࿰c;找不到则返回 -1
class="token keyword">int class="token function">binary_searchclass="token punctuation">(class="token keyword">int arrclass="token punctuation">[class="token punctuation">]class="token punctuation">, class="token keyword">int sizeclass="token punctuation">, class="token keyword">int targetclass="token punctuation">) class="token punctuation">{class="token keyword">int low class="token operator">= class="token number">0class="token punctuation">;class="token keyword">int high class="token operator">= size class="token operator">- class="token number">1class="token punctuation">;class="token keyword">while class="token punctuation">(low class="token operator"><= highclass="token punctuation">) class="token punctuation">{class="token comment">// 计算中间索引class="token keyword">int mid class="token operator">= low class="token operator">+ class="token punctuation">(high class="token operator">- lowclass="token punctuation">) class="token operator">/ class="token number">2class="token punctuation">;class="token comment">// 比较中间值与目标值class="token keyword">if class="token punctuation">(arrclass="token punctuation">[midclass="token punctuation">] class="token operator">== targetclass="token punctuation">) class="token punctuation">{class="token keyword">return midclass="token punctuation">;  class="token comment">// 找到目标值࿰c;返回索引class="token punctuation">} class="token keyword">else class="token keyword">if class="token punctuation">(arrclass="token punctuation">[midclass="token punctuation">] class="token operator">< targetclass="token punctuation">) class="token punctuation">{low class="token operator">= mid class="token operator">+ class="token number">1class="token punctuation">;  class="token comment">// 目标值在右半部分class="token punctuation">} class="token keyword">else class="token punctuation">{high class="token operator">= mid class="token operator">- class="token number">1class="token punctuation">;  class="token comment">// 目标值在左半部分class="token punctuation">}class="token punctuation">}class="token keyword">return class="token operator">-class="token number">1class="token punctuation">;  class="token comment">// 未找到目标值
class="token punctuation">}class="token keyword">int class="token function">mainclass="token punctuation">(class="token punctuation">) class="token punctuation">{class="token keyword">int arrclass="token punctuation">[class="token punctuation">] class="token operator">= class="token punctuation">{class="token number">1class="token punctuation">, class="token number">3class="token punctuation">, class="token number">7class="token punctuation">, class="token number">9class="token punctuation">, class="token number">12class="token punctuation">, class="token number">15class="token punctuation">, class="token number">18class="token punctuation">, class="token number">20class="token punctuation">, class="token number">23class="token punctuation">, class="token number">27class="token punctuation">}class="token punctuation">;class="token keyword">int size class="token operator">= class="token keyword">sizeofclass="token punctuation">(arrclass="token punctuation">) class="token operator">/ class="token keyword">sizeofclass="token punctuation">(arrclass="token punctuation">[class="token number">0class="token punctuation">]class="token punctuation">)class="token punctuation">;class="token keyword">int target class="token operator">= class="token number">15class="token punctuation">;class="token keyword">int result class="token operator">= class="token function">binary_searchclass="token punctuation">(arrclass="token punctuation">, sizeclass="token punctuation">, targetclass="token punctuation">)class="token punctuation">;class="token keyword">if class="token punctuation">(result class="token operator">!= class="token operator">-class="token number">1class="token punctuation">) class="token punctuation">{class="token function">printfclass="token punctuation">(class="token string">"目标值 %d 在数组中的索引是 %d\n"class="token punctuation">, targetclass="token punctuation">, resultclass="token punctuation">)class="token punctuation">;class="token punctuation">} class="token keyword">else class="token punctuation">{class="token function">printfclass="token punctuation">(class="token string">"目标值 %d 未在数组中找到\n"class="token punctuation">, targetclass="token punctuation">)class="token punctuation">;class="token punctuation">}class="token keyword">return class="token number">0class="token punctuation">;
class="token punctuation">}
code>

代码分析

  • 输入:有序数组 <code>arrcode>、数组大小 <code>sizecode>、要查找的目标值 <code>targetcode>。
  • 输出:如果找到目标值࿰c;返回它的索引;否则返回 <code>-1code>。
  • 核心逻辑:在 <code>whilecode> 循环中不断将搜索范围对半缩小࿰c;直到找到目标值或搜索范围为空。

class="tags" href="/SuanFa.html" title=算法>算法复杂度

  • 时间复杂度:O(log n)࿰c;因为每次都将搜索范围减半。
  • 空间复杂度:O(1)࿰c;只使用了固定的几个变量。

二分答案

“二分答案”其实是指通过不断缩小一个区间࿰c;直到找到最优解的思想。这不仅仅适用于查找某个特定值࿰c;还可以用于在某个区间内寻找一个最优解(例如最大值、最小值、最优条件等)。这种方法常见于优化问题数学问题中࿰c;通过二分法逐步逼近目标答案。

二分答案的过程:

  1. 初始化一个区间:首先确定一个包含所有可能答案的初始区间(比如 [L, R])。
  2. 中间点:每次将区间一分为二࿰c;计算出中间点(mid)。
  3. 判断最优方向:根据具体问题的特性࿰c;判断中间点是否满足最优解。如果没有满足࿰c;通过某种条件判断应该向左或向右缩小区间。
  4. 缩小区间:不断缩小范围࿰c;将搜索区间一分为二࿰c;直到区间足够小或者满足给定的精度。
  5. 最终解:当区间足够小࿰c;就能确定最优解在这个区间中。

通俗举例:

  • 假设你要找一个水池的最低点࿰c;水池有一个连续的坡度变化࿰c;你可以通过二分法来逐步缩小范围。首先在两端测试࿰c;判断哪个方向会更低࿰c;然后每次在中间位置进行测试࿰c;逐步将范围缩小࿰c;直到找到最低点。

适用场景:

  • 最小化/最大化问题:例如在某个范围内找到最小值或最大值。
  • 临界问题:找到一个满足某个条件的临界值࿰c;例如寻找某个值恰好满足某个约束条件。

这种“二分答案”思想应用广泛࿰c;用于求解具有连续性或单调性的问题࿰c;通过逐步缩小范围逼近最优解。

编程实现二分答案

下面是一个二分答案的C代码示例࿰c;解决了一个简单的最小值问题࿰c;并在每次迭代时使用 <code>printfcode> 打印出相关变量的值。

假设我们在某个范围内寻找一个满足条件的最小值࿰c;并逐步缩小范围直到找到答案。例子中࿰c;我们要找到某个数 <code>xcode>࿰c;使得 <code>x*xcode> 大于等于一个给定值 <code>targetcode>。

C代码示例:

<code class="prism language-c">class="token macro property">class="token directive-hash">#class="token directive keyword">include class="token string"><stdio.h>class="token keyword">int class="token function">binary_answerclass="token punctuation">(class="token keyword">int targetclass="token punctuation">) class="token punctuation">{class="token keyword">int low class="token operator">= class="token number">0class="token punctuation">;class="token keyword">int high class="token operator">= targetclass="token punctuation">;class="token keyword">int ans class="token operator">= class="token operator">-class="token number">1class="token punctuation">;class="token keyword">while class="token punctuation">(low class="token operator"><= highclass="token punctuation">) class="token punctuation">{class="token keyword">int mid class="token operator">= low class="token operator">+ class="token punctuation">(high class="token operator">- lowclass="token punctuation">) class="token operator">/ class="token number">2class="token punctuation">;class="token comment">// 打印调试信息class="token function">printfclass="token punctuation">(class="token string">"low = %d, high = %d, mid = %d, mid*mid = %d\n"class="token punctuation">, lowclass="token punctuation">, highclass="token punctuation">, midclass="token punctuation">, mid class="token operator">* midclass="token punctuation">)class="token punctuation">;class="token comment">// 判断 mid*mid 是否满足条件class="token keyword">if class="token punctuation">(mid class="token operator">* mid class="token operator">>= targetclass="token punctuation">) class="token punctuation">{ans class="token operator">= midclass="token punctuation">;   class="token comment">// 当前 mid 可能是答案high class="token operator">= mid class="token operator">- class="token number">1class="token punctuation">;  class="token comment">// 尝试更小的 midclass="token punctuation">} class="token keyword">else class="token punctuation">{low class="token operator">= mid class="token operator">+ class="token number">1class="token punctuation">;  class="token comment">// mid*mid 太小࿰c;尝试更大的 midclass="token punctuation">}class="token punctuation">}class="token keyword">return ansclass="token punctuation">;  class="token comment">// 返回满足条件的最小解
class="token punctuation">}class="token keyword">int class="token function">mainclass="token punctuation">(class="token punctuation">) class="token punctuation">{class="token keyword">int target class="token operator">= class="token number">25class="token punctuation">;  class="token comment">// 要寻找 x*x >= 25 的最小 xclass="token keyword">int result class="token operator">= class="token function">binary_answerclass="token punctuation">(targetclass="token punctuation">)class="token punctuation">;class="token function">printfclass="token punctuation">(class="token string">"满足条件的最小值是 %d\n"class="token punctuation">, resultclass="token punctuation">)class="token punctuation">;class="token keyword">return class="token number">0class="token punctuation">;
class="token punctuation">}
code>

输出示例:

当运行这个代码时࿰c;<code>printfcode> 会打印每次循环中的 <code>lowcode>、<code>highcode>、<code>midcode> 以及 <code>mid*midcode> 的值。假设 <code>target = 25code>࿰c;输出结果如下:

<code>low = 0, high = 25, mid = 12, mid*mid = 144
low = 0, high = 11, mid = 5, mid*mid = 25
low = 0, high = 4, mid = 2, mid*mid = 4
low = 3, high = 4, mid = 3, mid*mid = 9
low = 4, high = 4, mid = 4, mid*mid = 16
low = 5, high = 4, mid = 5, mid*mid = 25
满足条件的最小值是 5
code>

代码说明:

  • <code>lowcode> 和 <code>highcode> 初始化为搜索范围的起始值和目标值。
  • <code>midcode> 是每次迭代时的中间点࿰c;通过 <code>low + (high - low) / 2code> 计算。
  • 我们通过 <code>mid * midcode> 和 <code>targetcode> 比较来判断是否满足条件。
    • 如果 <code>mid * mid >= targetcode>࿰c;说明 <code>midcode> 可能是答案࿰c;并继续尝试更小的值。
    • 如果 <code>mid * mid < targetcode>࿰c;则调整 <code>lowcode> 向右侧逼近。
  • 最终返回满足 <code>mid * mid >= targetcode> 的最小 <code>midcode>。

这个二分答案的例子展示了如何通过逐步缩小区间找到满足条件的最优解࿰c;并通过 <code>printfcode> 输出了每次迭代中的关键变量。


总结

二分查找与二分答案是class="tags" href="/SuanFa.html" title=算法>算法中的重要工具࿰c;其高效性源于每次都将搜索范围对半分࿰c;快速缩小查找范围。在CSP-J考试的class="tags" href="/SuanFa.html" title=算法>算法基础部分࿰c;掌握这两种二分思想的应用࿰c;不仅仅局限于查找问题࿰c;还可以处理复杂的最优解和临界值问题。通过熟练运用二分查找࿰c;我们能够以 O(log N) 的时间复杂度解决诸多问题࿰c;提升class="tags" href="/SuanFa.html" title=算法>算法效率࿰c;成为解决CSP-J比赛题目中的重要武器。

class="blog-extension-box">

http://www.ppmy.cn/news/1527565.html

相关文章

SQL进阶技巧:如何将字符串数组清洗为简单map结构? | translate + regexp_replace方法

目录 0 场景描述 1 数据准备 2 问题分析 2.1 方法1 特征法-通用解法 2.2 方法2枚举法(不通用) 3 小结 ~~END~~ 如果觉得本文对你有帮助,那么不妨也可以选择去看看我的博客专栏 ,部分内容如下: 数字化建设通关指南专栏原价99,现在活动价29.9,按照阶梯式增长,直到恢…

OpenHarmony(鸿蒙南向开发)——标准系统方案之瑞芯微RK3568移植案例(上)

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ OpenHarmony&#xff08;鸿蒙南向开发&#xff09;——轻量和小型系统三方库移植指南…

天融信把桌面explorer.exe删了,导致开机之后无windows桌面,只能看到鼠标解决方法

win10开机进入桌面&#xff0c;发现桌面无了&#xff0c;但是可以ctrlaltdelete调出任务管理器 用管理员权限打开cmd&#xff0c;输入&#xff1a; sfc /scanfilec:\windowslexplorer.exe 在运行C:\windows\Explorer.exe&#xff1b;可以进入桌面&#xff0c;但是隔离几秒钟…

QT开发:深入详解QtCore模块事件处理,一文学懂QT 事件循环与处理机制

Qt 是一个跨平台的 C 应用程序框架&#xff0c;QtCore 模块提供了核心的非 GUI 功能。事件处理是 Qt 应用程序的重要组成部分。Qt 的事件处理机制包括事件循环和事件处理&#xff0c;它们共同确保应用程序能够响应用户输入、定时器事件和其他事件。 1. 事件循环&#xff08;Ev…

作业帮大数据面试题及参考答案

HashMap 和 HashTable 的区别是什么&#xff1f; HashMap 和 HashTable 都是 Java 中用于存储键值对的数据结构&#xff0c;但它们之间存在一些重要的区别&#xff1a; 线程安全性&#xff1a; HashTable 是线程安全的&#xff0c;它的方法都被 synchronized 关键字修饰&#x…

docker镜像源

目前国内可用Docker镜像源汇总&#xff08;截至2024年8月&#xff09; - CoderJia docker.registry.cyou正常docker-cf.registry.cyou正常dockerpull.com正常dockerproxy.cn正常docker.1panel.live正常hub.rat.dev正常dhub.kubesre.xyz正常docker.hlyun.org正常docker.kejilio…

硬件基础知识

驱动开发分为&#xff1a;裸机驱动、linux驱动 嵌入式&#xff1a;以计算机技术为基础&#xff0c;软硬结合的、可移植、可剪裁的专用计算机 单片机最小单元&#xff1a;vcc gnd reset 晶振 cpu --- soc :system on chip 片上外设 所有的程序都是在soc&#xff08;cpu&…

搭建rt-thread开发环境(makefile + cubemax)

编译器问题&#xff1a; topeetubuntu:~/rtthread-test$ make mkdir build arm-none-eabi-gcc -c -mcpucortex-m0plus -mthumb -DUSE_HAL_DRIVER -DSTM32G0B1xx -IRT-Thread -ICore/Inc -IDrivers/STM32G0xx_HAL_Driver/Inc -IDrivers/STM32G0xx_HAL_Driver/Inc/Legacy -I…