This commit is contained in:
krahets
2023-08-22 13:50:24 +08:00
parent 77b90cd19b
commit b70b7c9e75
67 changed files with 580 additions and 580 deletions
@@ -3479,13 +3479,13 @@
<li><strong>分(划分阶段)</strong>:递归地将原问题分解为两个或多个子问题,直至到达最小子问题时终止。</li>
<li><strong>治(合并阶段)</strong>:从已知解的最小子问题开始,从底至顶地将子问题的解进行合并,从而构建出原问题的解。</li>
</ol>
<p>图所示,“归并排序”是分治策略的典型应用之一,其算法原理为:</p>
<p>如图 12-1 所示,“归并排序”是分治策略的典型应用之一,其算法原理为:</p>
<ol>
<li><strong></strong>:递归地将原数组(原问题)划分为两个子数组(子问题),直到子数组只剩一个元素(最小子问题)。</li>
<li><strong></strong>:从底至顶地将有序的子数组(子问题的解)进行合并,从而得到有序的原数组(原问题的解)。</li>
</ol>
<p><img alt="归并排序的分治策略" src="../divide_and_conquer.assets/divide_and_conquer_merge_sort.png" /></p>
<p align="center">归并排序的分治策略 </p>
<p align="center"> 12-1 &nbsp; 归并排序的分治策略 </p>
<h2 id="1211">12.1.1 &nbsp; 如何判断分治问题<a class="headerlink" href="#1211" title="Permanent link">&para;</a></h2>
<p>一个问题是否适合使用分治解决,通常可以参考以下几个判断依据:</p>
@@ -3504,12 +3504,12 @@
<p>分治不仅可以有效地解决算法问题,<strong>往往还可以带来算法效率的提升</strong>。在排序算法中,快速排序、归并排序、堆排序相较于选择、冒泡、插入排序更快,就是因为它们应用了分治策略。</p>
<p>那么,我们不禁发问:<strong>为什么分治可以提升算法效率,其底层逻辑是什么</strong>?换句话说,将大问题分解为多个子问题、解决子问题、将子问题的解合并为原问题的解,这几步的效率为什么比直接解决原问题的效率更高?这个问题可以从操作数量和并行计算两方面来讨论。</p>
<h3 id="1">1. &nbsp; 操作数量优化<a class="headerlink" href="#1" title="Permanent link">&para;</a></h3>
<p>以“冒泡排序”为例,其处理一个长度为 <span class="arithmatex">\(n\)</span> 的数组需要 <span class="arithmatex">\(O(n^2)\)</span> 时间。假设我们按照图所示的方式,将数组从中点分为两个子数组,则划分需要 <span class="arithmatex">\(O(n)\)</span> 时间,排序每个子数组需要 <span class="arithmatex">\(O((n / 2)^2)\)</span> 时间,合并两个子数组需要 <span class="arithmatex">\(O(n)\)</span> 时间,总体时间复杂度为:</p>
<p>以“冒泡排序”为例,其处理一个长度为 <span class="arithmatex">\(n\)</span> 的数组需要 <span class="arithmatex">\(O(n^2)\)</span> 时间。假设我们按照图 12-2 所示的方式,将数组从中点分为两个子数组,则划分需要 <span class="arithmatex">\(O(n)\)</span> 时间,排序每个子数组需要 <span class="arithmatex">\(O((n / 2)^2)\)</span> 时间,合并两个子数组需要 <span class="arithmatex">\(O(n)\)</span> 时间,总体时间复杂度为:</p>
<div class="arithmatex">\[
O(n + (\frac{n}{2})^2 \times 2 + n) = O(\frac{n^2}{2} + 2n)
\]</div>
<p><img alt="划分数组前后的冒泡排序" src="../divide_and_conquer.assets/divide_and_conquer_bubble_sort.png" /></p>
<p align="center">划分数组前后的冒泡排序 </p>
<p align="center"> 12-2 &nbsp; 划分数组前后的冒泡排序 </p>
<p>接下来,我们计算以下不等式,其左边和右边分别为划分前和划分后的操作总数:</p>
<div class="arithmatex">\[
@@ -3525,9 +3525,9 @@ n(n - 4) &amp; &gt; 0
<h3 id="2">2. &nbsp; 并行计算优化<a class="headerlink" href="#2" title="Permanent link">&para;</a></h3>
<p>我们知道,分治生成的子问题是相互独立的,<strong>因此通常可以并行解决</strong>。也就是说,分治不仅可以降低算法的时间复杂度,<strong>还有利于操作系统的并行优化</strong></p>
<p>并行优化在多核或多处理器的环境中尤其有效,因为系统可以同时处理多个子问题,更加充分地利用计算资源,从而显著减少总体的运行时间。</p>
<p>比如在图所示的“桶排序”中,我们将海量的数据平均分配到各个桶中,则可所有桶的排序任务分散到各个计算单元,完成后再进行结果合并。</p>
<p>比如在图 12-3 所示的“桶排序”中,我们将海量的数据平均分配到各个桶中,则可所有桶的排序任务分散到各个计算单元,完成后再进行结果合并。</p>
<p><img alt="桶排序的并行计算" src="../divide_and_conquer.assets/divide_and_conquer_parallel_computing.png" /></p>
<p align="center">桶排序的并行计算 </p>
<p align="center"> 12-3 &nbsp; 桶排序的并行计算 </p>
<h2 id="1213">12.1.3 &nbsp; 分治常见应用<a class="headerlink" href="#1213" title="Permanent link">&para;</a></h2>
<p>一方面,分治可以用来解决许多经典算法问题:</p>