You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -214,6 +230,7 @@ <h2 id="toc-title">Table of contents</h2>
214
230
<li><ahref="#linear-search" id="toc-linear-search" class="nav-link active" data-scroll-target="#linear-search"><spanclass="header-section-number">1.1</span> Linear Search</a></li>
215
231
<li><ahref="#min-and-max" id="toc-min-and-max" class="nav-link" data-scroll-target="#min-and-max"><spanclass="header-section-number">1.2</span> Min and Max</a></li>
<spanid="cb13-17"><ahref="#cb13-17" aria-hidden="true" tabindex="-1"></a><spanclass="cf">elif</span> x <spanclass="op"><</span> items[m]:</span>
376
-
<spanid="cb13-18"><ahref="#cb13-18" aria-hidden="true" tabindex="-1"></a> r <spanclass="op">=</span> m <spanclass="op">-</span><spanclass="dv">1</span></span>
<spanid="cb13-20"><ahref="#cb13-20" aria-hidden="true" tabindex="-1"></a> l <spanclass="op">=</span> m <spanclass="op">+</span><spanclass="dv">1</span></span>
<spanid="cb13-16"><ahref="#cb13-16" aria-hidden="true" tabindex="-1"></a><spanclass="cf">if</span> res <spanclass="op">==</span><spanclass="dv">0</span>:</span>
<spanid="cb13-18"><ahref="#cb13-18" aria-hidden="true" tabindex="-1"></a><spanclass="cf">elif</span> res <spanclass="op"><</span><spanclass="dv">0</span>:</span>
394
+
<spanid="cb13-19"><ahref="#cb13-19" aria-hidden="true" tabindex="-1"></a> r <spanclass="op">=</span> m <spanclass="op">-</span><spanclass="dv">1</span></span>
<spanid="cb13-21"><ahref="#cb13-21" aria-hidden="true" tabindex="-1"></a> l <spanclass="op">=</span> m <spanclass="op">+</span><spanclass="dv">1</span></span></code><buttontitle="Copy to Clipboard" class="code-copy-button"><iclass="bi"></i></button></pre></div>
<p>Standard binary search is excellent for determining if an element exists, but it provides no guarantees about which index is returned if the sequence contains duplicates. In many applications—such as range queries or maintaining a sorted list—we need to find the specific boundaries where an element resides or where it should be inserted to maintain order.</p>
409
+
<p>This is the problem of <strong>bisection</strong>. We define two variants: <code>bisect_left</code> and <code>bisect_right</code>.</p>
410
+
<p>The <code>bisect_left</code> function finds the first index where an element <code>x</code> could be inserted while maintaining the sorted order of the sequence. If <code>x</code> is already present, the insertion point will be before (to the left of) any existing entries. Effectively, it returns the index of the first element that is not “less than” <code>x</code>.</p>
<spanid="cb15-4"><ahref="#cb15-4" aria-hidden="true" tabindex="-1"></a><spanclass="cf">if</span> f <spanclass="kw">is</span><spanclass="va">None</span>:</span>
415
+
<spanid="cb15-5"><ahref="#cb15-5" aria-hidden="true" tabindex="-1"></a> f <spanclass="op">=</span> default_order</span>
<spanid="cb15-12"><ahref="#cb15-12" aria-hidden="true" tabindex="-1"></a> l <spanclass="op">=</span> m <spanclass="op">+</span><spanclass="dv">1</span></span>
<spanid="cb15-16"><ahref="#cb15-16" aria-hidden="true" tabindex="-1"></a><spanclass="cf">return</span> l</span></code><buttontitle="Copy to Clipboard" class="code-copy-button"><iclass="bi"></i></button></pre></div>
427
+
<p>The logic here is subtle: instead of returning immediately when an element matches, we keep narrowing the window until <code>l</code> and <code>r</code> meet. By setting <code>r = m</code> when <code>items[m] >= x</code>, we ensure the right boundary eventually settles on the first occurrence.</p>
428
+
<p>Conversely, <code>bisect_right</code> (sometimes called <code>bisect_upper</code>) finds the last possible insertion point. If <code>x</code> is present, the index returned will be after (to the right of) all existing entries. This is useful for finding the index of the first element that is strictly “greater than” <code>x</code>.</p>
<spanid="cb16-4"><ahref="#cb16-4" aria-hidden="true" tabindex="-1"></a><spanclass="cf">if</span> f <spanclass="kw">is</span><spanclass="va">None</span>:</span>
433
+
<spanid="cb16-5"><ahref="#cb16-5" aria-hidden="true" tabindex="-1"></a> f <spanclass="op">=</span> default_order</span>
<spanid="cb16-14"><ahref="#cb16-14" aria-hidden="true" tabindex="-1"></a> l <spanclass="op">=</span> m <spanclass="op">+</span><spanclass="dv">1</span></span>
<spanid="cb16-16"><ahref="#cb16-16" aria-hidden="true" tabindex="-1"></a><spanclass="cf">return</span> l</span></code><buttontitle="Copy to Clipboard" class="code-copy-button"><iclass="bi"></i></button></pre></div>
445
+
<p>In this variant, we only move the left boundary <code>l</code> forward if <code>x >= items[m]</code>, which pushes the search toward the end of a block of identical values.</p>
446
+
<p>Since both functions follow the same halving principle as standard binary search, their performance characteristics are identical:</p>
447
+
<ul>
448
+
<li><strong>Time Complexity</strong>: <spanclass="math inline">\(O(\log n)\)</span>, as we halve the search space in every iteration of the <code>while</code> loop.</li>
449
+
<li><strong>Space Complexity</strong>: <spanclass="math inline">\(O(1)\)</span>, as we only maintain two integer indices regardless of the input size.</li>
450
+
</ul>
451
+
<p>To ensure these boundaries are calculated correctly, especially with duplicate elements, we use the following test cases:</p>
<spanid="cb17-13"><ahref="#cb17-13" aria-hidden="true" tabindex="-1"></a><spanclass="co"># If element is missing, both return the same insertion point</span></span>
0 commit comments