Skip to content

Commit e42a247

Browse files
committed
Built site for gh-pages
1 parent 94eb968 commit e42a247

5 files changed

Lines changed: 161 additions & 27 deletions

File tree

.nojekyll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0ee6daea
1+
007d49aa

01_search.html

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,27 @@
195195
</li>
196196
<li class="sidebar-item sidebar-item-section">
197197
<span class="sidebar-item-text sidebar-link text-start">
198+
<span class="menu-text">Fundamental Data Structures</span></span>
199+
</li>
200+
<li class="sidebar-item sidebar-item-section">
201+
<span class="sidebar-item-text sidebar-link text-start">
198202
<span class="menu-text">Trees</span></span>
199203
</li>
200204
<li class="sidebar-item sidebar-item-section">
201205
<span class="sidebar-item-text sidebar-link text-start">
202206
<span class="menu-text">Graphs</span></span>
207+
</li>
208+
<li class="sidebar-item sidebar-item-section">
209+
<span class="sidebar-item-text sidebar-link text-start">
210+
<span class="menu-text">Dynamic Programming and Greedy Algorithms</span></span>
211+
</li>
212+
<li class="sidebar-item sidebar-item-section">
213+
<span class="sidebar-item-text sidebar-link text-start">
214+
<span class="menu-text">Specialized Domains</span></span>
215+
</li>
216+
<li class="sidebar-item sidebar-item-section">
217+
<span class="sidebar-item-text sidebar-link text-start">
218+
<span class="menu-text">Advanced Complexity</span></span>
203219
</li>
204220
</ul>
205221
</div>
@@ -214,6 +230,7 @@ <h2 id="toc-title">Table of contents</h2>
214230
<li><a href="#linear-search" id="toc-linear-search" class="nav-link active" data-scroll-target="#linear-search"><span class="header-section-number">1.1</span> Linear Search</a></li>
215231
<li><a href="#min-and-max" id="toc-min-and-max" class="nav-link" data-scroll-target="#min-and-max"><span class="header-section-number">1.2</span> Min and Max</a></li>
216232
<li><a href="#binary-search" id="toc-binary-search" class="nav-link" data-scroll-target="#binary-search"><span class="header-section-number">1.3</span> Binary Search</a></li>
233+
<li><a href="#bisection" id="toc-bisection" class="nav-link" data-scroll-target="#bisection"><span class="header-section-number">1.4</span> Bisection</a></li>
217234
</ul>
218235
</nav>
219236
</div>
@@ -368,16 +385,15 @@ <h2 data-number="1.3" class="anchored" data-anchor-id="binary-search"><span clas
368385
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a> l, r <span class="op">=</span> <span class="dv">0</span>, <span class="bu">len</span>(items)<span class="op">-</span><span class="dv">1</span></span>
369386
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a></span>
370387
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> l <span class="op">&lt;=</span> r:</span>
371-
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a> m <span class="op">=</span> (l<span class="op">+</span>r)<span class="op">//</span><span class="dv">2</span></span>
372-
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a></span>
373-
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> items[m] <span class="op">==</span> x:</span>
374-
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> m</span>
375-
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">elif</span> x <span class="op">&lt;</span> items[m]:</span>
376-
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a> r <span class="op">=</span> m <span class="op">-</span> <span class="dv">1</span></span>
377-
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span>:</span>
378-
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true" tabindex="-1"></a> l <span class="op">=</span> m <span class="op">+</span> <span class="dv">1</span></span>
379-
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true" tabindex="-1"></a></span>
380-
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
388+
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a> m <span class="op">=</span> (l <span class="op">+</span> r) <span class="op">//</span> <span class="dv">2</span></span>
389+
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a> res <span class="op">=</span> f(x, items[m])</span>
390+
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a></span>
391+
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> res <span class="op">==</span> <span class="dv">0</span>:</span>
392+
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> m</span>
393+
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">elif</span> res <span class="op">&lt;</span> <span class="dv">0</span>:</span>
394+
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true" tabindex="-1"></a> r <span class="op">=</span> m <span class="op">-</span> <span class="dv">1</span></span>
395+
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span>:</span>
396+
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true" tabindex="-1"></a> l <span class="op">=</span> m <span class="op">+</span> <span class="dv">1</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
381397
<p>Here is a minimal test.</p>
382398
<div class="sourceCode" id="cb14" data-export="tests/search/test_binary.py"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> codex.search.binary <span class="im">import</span> binary_search</span>
383399
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a></span>
@@ -386,6 +402,73 @@ <h2 data-number="1.3" class="anchored" data-anchor-id="binary-search"><span clas
386402
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a></span>
387403
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> binary_search(<span class="dv">3</span>, items) <span class="op">==</span> <span class="dv">3</span></span>
388404
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> binary_search(<span class="dv">10</span>, items) <span class="kw">is</span> <span class="va">None</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
405+
</section>
406+
<section id="bisection" class="level2" data-number="1.4">
407+
<h2 data-number="1.4" class="anchored" data-anchor-id="bisection"><span class="header-section-number">1.4</span> Bisection</h2>
408+
<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>
411+
<div class="sourceCode" id="cb15" data-export="src/codex/search/binary.py"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> bisect_left[T](</span>
412+
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> x: T, items: Sequence[T], f: Ordering[T] <span class="op">=</span> <span class="va">None</span></span>
413+
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
414+
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> f <span class="kw">is</span> <span class="va">None</span>:</span>
415+
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a> f <span class="op">=</span> default_order</span>
416+
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a></span>
417+
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a> l, r <span class="op">=</span> <span class="dv">0</span>, <span class="bu">len</span>(items)</span>
418+
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a></span>
419+
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> l <span class="op">&lt;</span> r:</span>
420+
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a> m <span class="op">=</span> (l <span class="op">+</span> r) <span class="op">//</span> <span class="dv">2</span></span>
421+
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> f(items[m], x) <span class="op">&lt;</span> <span class="dv">0</span>:</span>
422+
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true" tabindex="-1"></a> l <span class="op">=</span> m <span class="op">+</span> <span class="dv">1</span></span>
423+
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span>:</span>
424+
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true" tabindex="-1"></a> r <span class="op">=</span> m</span>
425+
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true" tabindex="-1"></a></span>
426+
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> l</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="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] &gt;= 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>
429+
<div class="sourceCode" id="cb16" data-export="src/codex/search/binary.py"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> bisect_right[T](</span>
430+
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a> x: T, items: Sequence[T], f: Ordering[T] <span class="op">=</span> <span class="va">None</span></span>
431+
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> <span class="bu">int</span>:</span>
432+
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> f <span class="kw">is</span> <span class="va">None</span>:</span>
433+
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a> f <span class="op">=</span> default_order</span>
434+
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a></span>
435+
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a> l, r <span class="op">=</span> <span class="dv">0</span>, <span class="bu">len</span>(items)</span>
436+
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a></span>
437+
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">while</span> l <span class="op">&lt;</span> r:</span>
438+
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a> m <span class="op">=</span> (l <span class="op">+</span> r) <span class="op">//</span> <span class="dv">2</span></span>
439+
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> f(x, items[m]) <span class="op">&lt;</span> <span class="dv">0</span>:</span>
440+
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a> r <span class="op">=</span> m</span>
441+
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span>:</span>
442+
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a> l <span class="op">=</span> m <span class="op">+</span> <span class="dv">1</span></span>
443+
<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a></span>
444+
<span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> l</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
445+
<p>In this variant, we only move the left boundary <code>l</code> forward if <code>x &gt;= 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>: <span class="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>: <span class="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>
452+
<div class="sourceCode" id="cb17" data-export="tests/search/test_binary.py"><pre class="sourceCode python code-with-copy"><code class="sourceCode python"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> codex.search.binary <span class="im">import</span> bisect_left, bisect_right</span>
453+
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a></span>
454+
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> test_bisection_boundaries():</span>
455+
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a> <span class="co"># Sequence with a "block" of 2s</span></span>
456+
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a> items <span class="op">=</span> [<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">2</span>, <span class="dv">2</span>, <span class="dv">3</span>]</span>
457+
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a></span>
458+
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a> <span class="co"># First index where 2 is (or could be)</span></span>
459+
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> bisect_left(<span class="dv">2</span>, items) <span class="op">==</span> <span class="dv">1</span></span>
460+
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a></span>
461+
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a> <span class="co"># Index after the last 2</span></span>
462+
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> bisect_right(<span class="dv">2</span>, items) <span class="op">==</span> <span class="dv">4</span></span>
463+
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a></span>
464+
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a> <span class="co"># If element is missing, both return the same insertion point</span></span>
465+
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> bisect_left(<span class="fl">1.5</span>, items) <span class="op">==</span> <span class="dv">1</span></span>
466+
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> bisect_right(<span class="fl">1.5</span>, items) <span class="op">==</span> <span class="dv">1</span></span>
467+
<span id="cb17-16"><a href="#cb17-16" aria-hidden="true" tabindex="-1"></a></span>
468+
<span id="cb17-17"><a href="#cb17-17" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> test_bisection_extremes():</span>
469+
<span id="cb17-18"><a href="#cb17-18" aria-hidden="true" tabindex="-1"></a> items <span class="op">=</span> [<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>]</span>
470+
<span id="cb17-19"><a href="#cb17-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> bisect_left(<span class="dv">0</span>, items) <span class="op">==</span> <span class="dv">0</span></span>
471+
<span id="cb17-20"><a href="#cb17-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> bisect_right(<span class="dv">4</span>, items) <span class="op">==</span> <span class="dv">3</span></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
389472

390473

391474
</section>

The-Algorithm-Codex.pdf

13.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)