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

View File

@ -3427,7 +3427,7 @@
<p>给定一个长度为 <span class="arithmatex">\(n\)</span> 的有序数组 <code>nums</code> 和一个元素 <code>target</code> ,数组不存在重复元素。现将 <code>target</code> 插入到数组 <code>nums</code> 中,并保持其有序性。若数组中已存在元素 <code>target</code> ,则插入到其左方。请返回插入后 <code>target</code> 在数组中的索引。</p>
</div>
<p><img alt="二分查找插入点示例数据" src="../binary_search_insertion.assets/binary_search_insertion_example.png" /></p>
<p align="center">二分查找插入点示例数据 </p>
<p align="center"> 10-4 &nbsp; 二分查找插入点示例数据 </p>
<p>如果想要复用上节的二分查找代码,则需要回答以下两个问题。</p>
<p><strong>问题一</strong>:当数组中包含 <code>target</code> 时,插入点的索引是否是该元素的索引?</p>
@ -3580,16 +3580,16 @@
<p>在上一题的基础上,规定数组可能包含重复元素,其余不变。</p>
</div>
<p>假设数组中存在多个 <code>target</code> ,则普通二分查找只能返回其中一个 <code>target</code> 的索引,<strong>而无法确定该元素的左边和右边还有多少 <code>target</code></strong></p>
<p>题目要求将目标元素插入到最左边,<strong>所以我们需要查找数组中最左一个 <code>target</code> 的索引</strong>。初步考虑通过图所示的步骤实现。</p>
<p>题目要求将目标元素插入到最左边,<strong>所以我们需要查找数组中最左一个 <code>target</code> 的索引</strong>。初步考虑通过图 10-5 所示的步骤实现。</p>
<ol>
<li>执行二分查找,得到任意一个 <code>target</code> 的索引,记为 <span class="arithmatex">\(k\)</span></li>
<li>从索引 <span class="arithmatex">\(k\)</span> 开始,向左进行线性遍历,当找到最左边的 <code>target</code> 时返回。</li>
</ol>
<p><img alt="线性查找重复元素的插入点" src="../binary_search_insertion.assets/binary_search_insertion_naive.png" /></p>
<p align="center">线性查找重复元素的插入点 </p>
<p align="center"> 10-5 &nbsp; 线性查找重复元素的插入点 </p>
<p>此方法虽然可用,但其包含线性查找,因此时间复杂度为 <span class="arithmatex">\(O(n)\)</span> 。当数组中存在很多重复的 <code>target</code> 时,该方法效率很低。</p>
<p>现考虑拓展二分查找代码。如图所示,整体流程保持不变,每轮先计算中点索引 <span class="arithmatex">\(m\)</span> ,再判断 <code>target</code><code>nums[m]</code> 大小关系:</p>
<p>现考虑拓展二分查找代码。如图 10-6 所示,整体流程保持不变,每轮先计算中点索引 <span class="arithmatex">\(m\)</span> ,再判断 <code>target</code><code>nums[m]</code> 大小关系:</p>
<ol>
<li><code>nums[m] &lt; target</code><code>nums[m] &gt; target</code> 时,说明还没有找到 <code>target</code> ,因此采用普通二分查找的缩小区间操作,<strong>从而使指针 <span class="arithmatex">\(i\)</span><span class="arithmatex">\(j\)</span><code>target</code> 靠近</strong></li>
<li><code>nums[m] == target</code> 时,说明小于 <code>target</code> 的元素在区间 <span class="arithmatex">\([i, m - 1]\)</span> 中,因此采用 <span class="arithmatex">\(j = m - 1\)</span> 来缩小区间,<strong>从而使指针 <span class="arithmatex">\(j\)</span> 向小于 <code>target</code> 的元素靠近</strong></li>
@ -3623,7 +3623,7 @@
</div>
</div>
</div>
<p align="center">二分查找重复元素的插入点的步骤 </p>
<p align="center"> 10-6 &nbsp; 二分查找重复元素的插入点的步骤 </p>
<p>观察以下代码,判断分支 <code>nums[m] &gt; target</code><code>nums[m] == target</code> 的操作相同,因此两者可以合并。</p>
<p>即便如此,我们仍然可以将判断条件保持展开,因为其逻辑更加清晰、可读性更好。</p>