content_views"
class="markdown_views prism-atom-one-dark">
前言
在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;提升效率。
二分思想的工作原理
- 初始状态:假设有一个已经排序的数组或问题空间c;我们要在其中查找某个目标元素或特定的解。
- 中间点选择:计算当前区间的中间位置c;并将这个中间值与目标值进行比较。
- 范围缩小:
- 如果中间值等于目标值c;查找结束。
- 如果中间值大于目标值c;那么目标值一定在中间值左侧c;因此将搜索范围缩小到左半部分。
- 如果中间值小于目标值c;则将搜索范围缩小到右半部分。
- 重复上述过程c;每次将查找范围对半缩小c;直到找到目标值或查找范围为空。
二分思想的好处
- 高效性:二分查找的时间复杂度为 O(log n)c;远优于 O(n) 的线性查找。特别是在大规模数据集或搜索空间中c;二分法能够显著加快查找速度。
- 简洁性:class="tags" href="/SuanFa.html" title=算法>算法逻辑简单清晰c;易于理解和实现。
- 应用广泛:二分思想不仅可以用于数组查找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=算法>算法结束。
二分查找过程中的索引变化:
- 初始查找范围 <code>[0, 9]code>c;中间索引 <code>4code>c;比较结果 <code>arr[4] = 12code>。
- 缩小范围 <code>[5, 9]code>c;中间索引 <code>7code>c;比较结果 <code>arr[7] = 20code>。
- 缩小范围 <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;通过二分法逐步逼近目标答案。
二分答案的过程:
- 初始化一个区间:首先确定一个包含所有可能答案的初始区间(比如 [L, R])。
- 中间点:每次将区间一分为二c;计算出中间点(mid)。
- 判断最优方向:根据具体问题的特性c;判断中间点是否满足最优解。如果没有满足c;通过某种条件判断应该向左或向右缩小区间。
- 缩小区间:不断缩小范围c;将搜索区间一分为二c;直到区间足够小或者满足给定的精度。
- 最终解:当区间足够小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比赛题目中的重要武器。