<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged data-structures</title>
<link>https://coderlegion.com/tag/data-structures</link>
<description>Powered by Question2Answer</description>
<item>
<title>Arrays are dumb. So are tuples.</title>
<link>https://coderlegion.com/29090/arrays-are-dumb-so-are-tuples</link>
<description>&lt;p&gt;The array/tuple question was never meant to be a long running thing. And as it often happens, the solution was right there in front of me the whole time.&lt;/p&gt;
&lt;p&gt;Arrays are intuitive, fast, and let you do (basically) anything. Therein lies the problem with them, no type-constraint, no tracking (other than what you keep in your head).&lt;/p&gt;
&lt;p&gt;Tuples are almost exactly the opposite. Powerful, dependable (in and of themselves), type-safe at the position level. But they are awkward, and tend to make code that depends on them, more brittle.&lt;/p&gt;
&lt;p&gt;When a debate goes back and forth multiple times with NO NEW INFORMATION being submitted; it tends to devolve into an interminable loop. The only way to break the cycle is to add data, or perspective. The second is honestly the more powerful, by far.&lt;/p&gt;
&lt;p&gt;So, what is the perspective that this question is inspected from? &quot;a programming language needs arrays, tuples, or both.&quot; There, simply by stating what the subject is in clear terms, we can start to see that the problem is in the perspective itself.&lt;/p&gt;
&lt;p&gt;&quot;A programming language needs an ordered-sequence data-structure&quot; (hyphens were hand typed, mmm). Now just look at that! The solution practically bites you on the nose.&lt;/p&gt;
&lt;p&gt;Don't use arrays or tuples.&lt;/p&gt;
&lt;p&gt;A &quot;tape&quot; is a &quot;constrained by use-case&quot; data sequence. You can think of it as an array with a la carte permissions. With no permissions, a tape explicitly declared will remain exactly that forever.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scrml&quot;&gt;&amp;lt;primes:int[]=[2, 3, 5, 7]/&amp;gt;

@primes.push(11)                       // error: length is fixed
@primes[0] = 1                         // error: positions are read-only
@primes = [1, 2, 3]                    // error: replacing the whole thing is its own permission
const big = @primes.map(p =&amp;gt; p * 10)   // fine: map returns a new value
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The sequence itself is frozen: no appends, no removals, no reordering, no swapping one element for another. The elements are exactly as frozen as their own type says. If the type of the thing on the tape has a writable field, that field is writable, tape or no tape. Same rule, all the way down.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scrml&quot;&gt;&amp;lt;todo:struct&amp;gt;
    &amp;lt;title:string=&quot;&quot;/&amp;gt;
    &amp;lt;let done:bool=false/&amp;gt;       // this field grants its own write
&amp;lt;/&amp;gt;

&amp;lt;todos:todo[]=[...]/&amp;gt;            // a tape with no permissions

@todos[0].done = true            // fine: judged by `done`, not by the tape
@todos[0].title = &quot;x&quot;            // error: title is locked
@todos.push(t)                   // error: the tape is frozen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The obvious way to build this is a list of allowed methods: this tape may &lt;code&gt;push&lt;/code&gt;, that one may &lt;code&gt;pop&lt;/code&gt;. That breaks immediately. &lt;code&gt;@log = @log.slice(1)&lt;/code&gt; throws away the first entry using nothing but a non-mutating method and an assignment. No banned method was called. So a tape's permissions aren't a list of methods, they're axes: how the length may change, where changes may happen, whether a position may be rewritten. The compiler judges every write by what it DOES to the value, not by how it's spelled. &lt;code&gt;slice(1)&lt;/code&gt; is a change at the front. If the tape doesn't allow front changes, that line doesn't compile.&lt;/p&gt;
&lt;p&gt;Here's an audit log. Free length, changes only at the end, and nothing else:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scrml&quot;&gt;type Entry:struct = { at: number, actor: string, action: string }

&amp;lt;audit:Entry[free, end]=[]/&amp;gt;

@audit.push(entry)                          // fine
@audit = [...@audit, entry]                 // same edit, different spelling: fine
@audit.shift()                              // error: a change at the front
@audit = @audit.slice(1)                    // error: still a change at the front
@audit = @audit.filter(e =&amp;gt; e.actor != &quot;x&quot;) // error: a removal from anywhere
@audit = []                                 // error: a replace
reset(@audit)                               // error: reset is a replace too
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No statement anywhere in the program can remove or rewrite an entry, however it's spelled. That log is provably append-only, and the proof costs you one line.&lt;/p&gt;
&lt;p&gt;The permissions are part of the tape's type, so they travel. A function only gets to do what its parameter says:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scrml&quot;&gt;fn pushEdit(s: Edit[end], e: Edit) -&amp;gt; Edit[end] {
    return [...s, e]             // the only thing this function CAN do to `s`
}

&amp;lt;undo:Edit[end]=[]/&amp;gt;
@undo = pushEdit(@undo, e)       // fine: the compiler reads the body and sees an append at the end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There's no &quot;all permissions&quot; switch. A fully mutable tape has to name every axis it wants. An all-grant is a leaky abstraction, unacceptable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scrml&quot;&gt;&amp;lt;todos:Todo[free, anywhere, writable, replace]=[]/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every axis named. No shorthand. That's the point.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The tape is specified in the scrml language spec (§66.12) and is being built into the new compiler now. It is not in the shipping compiler yet, and the permission syntax shown here is the proposed spelling.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29090/arrays-are-dumb-so-are-tuples</guid>
<pubDate>Tue, 29 Sep 2026 14:43:13 +0000</pubDate>
</item>
<item>
<title>What Should Texas Businesses Look for in an ERP Software Development Company?</title>
<link>https://coderlegion.com/26792/what-should-texas-businesses-look-for-in-an-erp-software-development-company</link>
<description>&lt;p&gt;Texas has one of the most diverse business environments in the United States. Companies across energy, manufacturing, technology, healthcare, logistics, retail, construction, and professional services operate across the state. Because each industry has different operational requirements, choosing the right ERP technology partner requires careful evaluation.&lt;/p&gt;
&lt;p&gt;A good &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.matiyas.com/en-us/erp-solutions-in-texas/&quot;&gt;ERP software development company in Texas&lt;/a&gt;&lt;/strong&gt; should do more than provide software. It should understand business processes and help organizations create a technology environment that supports efficiency and future growth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Industry Understanding&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;ERP requirements vary significantly between industries.&lt;/p&gt;
&lt;p&gt;For example, an oil &amp;amp; gas company may need joint venture accounting, AFE management, production reporting, royalty calculations, and asset management. A manufacturer may prioritize production planning, BOM management, quality control, procurement, and inventory.&lt;/p&gt;
&lt;p&gt;Texas businesses should therefore look for an ERP partner with experience understanding industry-specific processes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Customization Capabilities&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Standard ERP functionality may cover many common processes, but organizations often have unique workflows.&lt;/p&gt;
&lt;p&gt;The ERP provider should be able to configure or customize workflows, reports, forms, modules, and integrations where necessary.&lt;/p&gt;
&lt;p&gt;The objective is to make the system useful for the business without introducing unnecessary complexity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Integration Experience&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Modern businesses rarely operate using one application.&lt;/p&gt;
&lt;p&gt;An ERP environment may need to connect with CRM, eCommerce, banking, payment, warehouse, IoT, or other systems.&lt;/p&gt;
&lt;p&gt;An experienced ERP partner should understand how these integrations can improve data flow and reduce duplicate work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Implementation Methodology&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A strong ERP platform can still fail if implementation is poorly managed.&lt;/p&gt;
&lt;p&gt;Businesses should ask potential providers about their approach to requirements gathering, configuration, data migration, testing, employee training, go-live, and post-launch support.&lt;/p&gt;
&lt;p&gt;A structured methodology can help reduce implementation risks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Scalability&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The ERP system should support future business growth.&lt;/p&gt;
&lt;p&gt;Texas organizations may expand into new markets, add locations, increase users, or introduce new products and services. The technology should be flexible enough to support these changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. Support and Training&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Employees need training to adopt a new ERP successfully. Businesses should also have access to technical support after implementation.&lt;/p&gt;
&lt;p&gt;Matiyas Solutions provides end-to-end &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.matiyas.com/en-us/erp-solutions-in-texas/&quot;&gt;&lt;strong&gt;ERP services&lt;/strong&gt;&lt;/a&gt; including consulting, implementation, customization, integration, training, support, and optimization for businesses across Texas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. Location-Specific Requirements&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Texas businesses may have requirements related to industries, taxation, payroll, healthcare privacy, cross-border trade, energy operations, and other regulatory considerations.&lt;/p&gt;
&lt;p&gt;An ERP provider that understands the Texas business environment can help organizations plan technology around these requirements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Choosing an ERP software development company in Texas should be based on more than price or a list of software features.&lt;/p&gt;
&lt;p&gt;Businesses should evaluate industry expertise, customization, integrations, implementation methodology, scalability, training, and ongoing support.&lt;/p&gt;
&lt;p&gt;With the right ERP partner, Texas organizations can build a connected technology environment that supports operational efficiency and long-term growth.&lt;/p&gt;
&lt;p&gt;visit -  &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.matiyas.com/en-us/erp-solutions-in-texas/&quot;&gt;https://www.matiyas.com/en-us/erp-solutions-in-texas/&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26792/what-should-texas-businesses-look-for-in-an-erp-software-development-company</guid>
<pubDate>Fri, 11 Sep 2026 07:54:10 +0000</pubDate>
</item>
<item>
<title>Backtracking Finally Clicked When I Fixed One Habit</title>
<link>https://coderlegion.com/18581/backtracking-finally-clicked-when-i-fixed-one-habit</link>
<description>&lt;p&gt;For a long stretch, backtracking was the topic I quietly dreaded. The recursion was easy enough to write. The code ran. It even returned answers. They were just wrong, and not in a way that threw an error you could chase. A Combination Sum solution would spit out duplicate-looking results with no obvious culprit line. The fix, when it came, had nothing to do with understanding recursion better. It was one habit that kept getting skipped.&lt;/p&gt;
&lt;p&gt;Here's what changed once backtracking stopped being a clever trick and became a fixed loop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What changed&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Backtracking is a depth first walk over a state space tree, and the tree is the part worth drawing first.&lt;/li&gt;
&lt;li&gt;The loop is always the same: choose an option, check the constraint, undo the choice on the way back up.&lt;/li&gt;
&lt;li&gt;The undo step is the easiest to drop, and dropping it silently corrupts later branches.&lt;/li&gt;
&lt;li&gt;Pruning before you recurse, not after you reach a leaf, is where the speed lives.&lt;/li&gt;
&lt;li&gt;Once the same partial state repeats across branches, the whole thing becomes top down DP.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;where-my-backtracking-kept-breaking&quot;&gt;Where my backtracking kept breaking&lt;/h3&gt;
&lt;p&gt;The mistake was always the same, and naming it took embarrassingly long. You build a partial solution by mutating a shared list, recurse, then move on to the next option without ever putting the list back the way you found it. So branch two starts with branch one's leftovers still inside it.&lt;/p&gt;
&lt;p&gt;What broke wasn't the recursion. It was that the path never got restored on the way back up.&lt;/p&gt;
&lt;p&gt;The reason it's so hard to catch: nothing crashes. A forgotten undo doesn't raise an exception. It quietly hands stale state to the next branch, and the answers look plausible until you check them by hand. That single omission costs more debugging time than any actual algorithmic concept.&lt;/p&gt;
&lt;p&gt;Picture it concretely on a small Combination Sum case, candidates &lt;code&gt;[2, 3]&lt;/code&gt; and target &lt;code&gt;5&lt;/code&gt;. The first branch explores &lt;code&gt;[2, 2, ...]&lt;/code&gt;, overshoots, and returns. If that branch left a stray &lt;code&gt;2&lt;/code&gt; in the shared list, the next branch starts from &lt;code&gt;[2]&lt;/code&gt; when it should start from &lt;code&gt;[]&lt;/code&gt;. Now the &lt;code&gt;[3, ...]&lt;/code&gt; branch is really exploring &lt;code&gt;[2, 3, ...]&lt;/code&gt;, and the result set fills with combinations that were never asked for. Reading the function top to bottom, every line looks reasonable. The bug only shows up when you trace the shared list across two sibling branches and notice it isn't the same length going in as coming out.&lt;/p&gt;
&lt;h3 id=&quot;what-i-started-doing-differently&quot;&gt;What I started doing differently&lt;/h3&gt;
&lt;p&gt;The shift was to stop writing backtracking freehand and write the same three line frame every time, then fill it in. Choose, check, undo, in that order, with the undo non negotiable. Here's the before and after, once it became a fixed habit:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Then &lt;/th&gt;&lt;th&gt;Now&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Recursion written ad hoc, undo when remembered &lt;/td&gt;&lt;td&gt;Choose, check, undo written as a fixed frame first&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Constraints checked at the leaf &lt;/td&gt;&lt;td&gt;Constraint checked before recursing, so dead branches die early&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Debugged by re-reading the whole function &lt;/td&gt;&lt;td&gt;Trace one branch, confirm state is restored after the call&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Each problem treated as new &lt;/td&gt;&lt;td&gt;Slot the problem into one of three known shapes&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &quot;check before recurse&quot; row mattered more than expected. Pruning at the leaf still gives correct answers, it's just slow, and on a tight time limit slow is the same as failing.&lt;/p&gt;
&lt;h3 id=&quot;what-that-looked-like-on-combination-sum&quot;&gt;What that looked like on Combination Sum&lt;/h3&gt;
&lt;p&gt;Combination Sum is the problem that finally made it click for me. Given a list of candidates and a target, find every combination that sums to the target, where a number can be reused. It's a conditional enumeration problem, so you prune any branch whose running sum overshoots.&lt;/p&gt;
&lt;p&gt;Here's the frame, filled in:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def combination_sum(candidates, target):
    result = []

    def backtrack(start, path, remaining):
        if remaining == 0:
            result.append(path[:])      # finished combination
            return
        if remaining &amp;lt; 0:
            return                      # overshot, prune this branch
        for i in range(start, len(candidates)):
            path.append(candidates[i])                       # choose
            backtrack(i, path, remaining - candidates[i])    # explore (reuse allowed)
            path.pop()                                       # undo

    backtrack(0, [], target)
    return result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The line that used to go missing is &lt;code&gt;path.pop()&lt;/code&gt;. Without it, every combination after the first carries elements from the previous one. The &lt;code&gt;path[:]&lt;/code&gt; matters too, it copies the list into the result instead of storing a reference that later &lt;code&gt;pop&lt;/code&gt; calls would mutate out from under you. Two small lines, and they're the entire difference between a function that works and one that lies to you.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;start&lt;/code&gt; index is the other quiet detail. Passing &lt;code&gt;i&lt;/code&gt; rather than &lt;code&gt;i + 1&lt;/code&gt; into the recursive call is what allows a number to be reused. Pass &lt;code&gt;i + 1&lt;/code&gt; and you've written a different problem, combinations without repetition. Same frame, one parameter changes the meaning.&lt;/p&gt;
&lt;h3 id=&quot;why-undoing-state-is-the-whole-game&quot;&gt;Why undoing state is the whole game&lt;/h3&gt;
&lt;p&gt;With the frame in hand, the three problem shapes read as one idea seen from different angles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/courses/backtracking/dIe_AzPs_9PsI92I4FLHc&quot;&gt;&lt;strong&gt;Unconditional enumeration&lt;/strong&gt;&lt;/a&gt;, like all subsets, has no constraint, so you walk the whole tree and every node is an answer.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/courses/backtracking/UUkyIeusIihAiyH8Xz9P8&quot;&gt;&lt;strong&gt;Conditional enumeration&lt;/strong&gt;&lt;/a&gt;, like Combination Sum or valid parentheses, prunes branches that break a rule mid build.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/courses/backtracking/rblzCNicLv5RvATRMpCWV&quot;&gt;&lt;strong&gt;Backtracking search&lt;/strong&gt;&lt;/a&gt;, like N-Queens or Sudoku, looks for configurations under a global constraint and prunes hardest.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All three run the same choose, check, undo loop. The undo is what lets one mutable structure stand in for every node of the tree without the nodes contaminating each other. That's the thing I ended up rebuilding my own approach around.&lt;/p&gt;
&lt;p&gt;It also reframes how the three shapes relate. They aren't three separate algorithms to learn. They're one loop with the constraint check dialled from off (subsets) to mid build (Combination Sum) to every level (N-Queens). Seeing them as one thing meant a new problem stopped being a blank page. The question became which shape it was and what the constraint was, and the frame handled the rest.&lt;/p&gt;
&lt;h3 id=&quot;what-i-d-tell-someone-stuck-on-this&quot;&gt;What I'd tell someone stuck on this&lt;/h3&gt;
&lt;p&gt;If you're losing time to backtracking that returns wrong answers, the fix is almost never more practice on harder problems. Grinding ten more search problems with the same blind spot just produces ten more silently wrong solutions. The thing standing between you and reliable backtracking is usually one missing line, not a missing concept. It's two habits. First, write the undo line at the same moment you write the choose line, as a pair, before anything else in the loop. Second, trace one full branch on paper and confirm the shared state looks identical before and after the recursive call.&lt;/p&gt;
&lt;p&gt;And if the recursion itself still feels unstable underneath all this, that's worth shoring up first, because backtracking is just recursion with the choose, check, undo wrapper on top. Going back through &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/courses/recursion&quot;&gt;the recursion lessons&lt;/a&gt; before touching backtracking again is what makes the wrapper feel mechanical instead of magical.&lt;/p&gt;
&lt;p&gt;I originally posted this on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/blogs/backtracking-algorithm-explained&quot;&gt;my own blog&lt;/a&gt;, a slightly longer version with the N-Queens trace and a few more worked examples.&lt;/p&gt;
&lt;p&gt;What's a bug that taught you more than the algorithm itself? For a lot of people backtracking is the one where a missing undo quietly breaks everything.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18581/backtracking-finally-clicked-when-i-fixed-one-habit</guid>
<pubDate>Thu, 21 May 2026 16:14:14 +0000</pubDate>
</item>
<item>
<title>AVL Trees Explained: The Rotation Patterns Most People Memorise Wrong</title>
<link>https://coderlegion.com/18486/avl-trees-explained-the-rotation-patterns-most-people-memorise-wrong</link>
<description>&lt;p&gt;The first time an interviewer asked me to walk through an LR rotation on a whiteboard, the four-case table was memorised cold. LL, RR, LR, RL. The interviewer drew three nodes, asked which case it was, and the answer came out fast. Then they asked: &quot;predict what a single right rotation would do to that tree, and tell me why it fails.&quot; That part hadn't been drilled. The grade went out as &quot;knows the table, didn't internalise the invariant.&quot;&lt;/p&gt;
&lt;h3 id=&quot;what-i-figured-out&quot;&gt;What I figured out&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The four rotation cases are not four things. They're two atomic moves applied once or twice.&lt;/li&gt;
&lt;li&gt;The balance factor is the rule being protected, not a check. Every rotation is a local repair on the rule &lt;code&gt;|left_height - right_height| &amp;lt;= 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Insertion needs at most one rotation. Deletion can need &lt;code&gt;O(log n)&lt;/code&gt;. That asymmetry is the answer to every &quot;what's the tradeoff&quot; question.&lt;/li&gt;
&lt;li&gt;The double-rotation cases exist because a single rotation on an &quot;inside&quot; imbalance shifts the heavy side without reducing the depth.&lt;/li&gt;
&lt;li&gt;Knowing the two atomic moves is enough to derive both LR and RL cases. Memorising the table on top is the wrong study target.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;where-my-approach-was-breaking&quot;&gt;Where my approach was breaking&lt;/h3&gt;
&lt;p&gt;Three failure modes showed up on those rounds, and they show up in most engineers preparing for the same kind of question.&lt;/p&gt;
&lt;p&gt;The four-case table got memorised without anyone tracing why each fix works. The table is a lookup. Interviewers test for the derivation. When a tree shows up on the board and the question is &quot;what's the imbalance shape here,&quot; pattern matching against a remembered picture fails when the tree is rotated or relabelled.&lt;/p&gt;
&lt;p&gt;Insertion got drilled, deletion didn't. Insertion is the cleaner operation. Almost every textbook walks insertion. Few walk deletion with the upward rebalance loop. Knowing &quot;deletion is harder&quot; as a phrase is not the same as knowing what concretely makes it harder.&lt;/p&gt;
&lt;p&gt;Wrong rotations never got sketched. When an interviewer asks &quot;what happens if you apply a single right rotation to an LR case,&quot; the answer has to come from a drawing, not a memory. Engineers who've never traced a failed rotation can't answer this on the spot.&lt;/p&gt;
&lt;h3 id=&quot;what-i-started-doing-differently-1&quot;&gt;What I started doing differently&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Then &lt;/th&gt;&lt;th&gt;Now&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Memorise LL / RR / LR / RL as four rotations &lt;/td&gt;&lt;td&gt;Internalise two atomic rotations and derive the four cases from balance factor signs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Practice insertion only &lt;/td&gt;&lt;td&gt;Practice deletion with the upward loop, including the worst-case &lt;code&gt;O(log n)&lt;/code&gt; rotation chain&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Treat balance factor as a check &lt;/td&gt;&lt;td&gt;Treat balance factor as the invariant being protected, with rotations as the local repair&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Draw rotated trees from memory &lt;/td&gt;&lt;td&gt;Draw the pointer assignments explicitly and recompute heights before moving on&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Skip wrong-rotation traces &lt;/td&gt;&lt;td&gt;Sketch what each wrong rotation does on an LR case, so the trap is recognisable under pressure&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &quot;now&quot; column is what interviewers actually probe. The &quot;then&quot; column is what most prep material asks you to drill.&lt;/p&gt;
&lt;p&gt;The first rule that locks in is this. If you see a node with balance factor &lt;code&gt;+2&lt;/code&gt;, the next look is at the left child's balance factor, not at the rotation table. If the left child is &lt;code&gt;+1&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt;, single right rotate the parent. If the left child is &lt;code&gt;-1&lt;/code&gt;, left rotate the left child first, then right rotate the parent. The decision is two questions, not a memorised table.&lt;/p&gt;
&lt;h3 id=&quot;what-that-looked-like-on-the-next-problem&quot;&gt;What that looked like on the next problem&lt;/h3&gt;
&lt;p&gt;Six months later, a different round asked for AVL insertion implementation. The sequence used to motivate the walk-through was &lt;code&gt;[5, 3, 4]&lt;/code&gt;, which fires an LR rotation. Here's the primitive:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def right_rotate(z):
    y = z.left
    T3 = y.right
    y.right = z
    z.left = T3
    z.height = 1 + max(height(z.left), height(z.right))
    y.height = 1 + max(height(y.left), height(y.right))
    return y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the insertion function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def insert(node, key):
    if not node:
        return Node(key)
    if key &amp;lt; node.key:
        node.left = insert(node.left, key)
    else:
        node.right = insert(node.right, key)

    node.height = 1 + max(height(node.left), height(node.right))
    bf = balance_factor(node)

    if bf &amp;gt; 1 and key &amp;lt; node.left.key:
        return right_rotate(node)
    if bf &amp;lt; -1 and key &amp;gt; node.right.key:
        return left_rotate(node)
    if bf &amp;gt; 1 and key &amp;gt; node.left.key:
        node.left = left_rotate(node.left)
        return right_rotate(node)
    if bf &amp;lt; -1 and key &amp;lt; node.right.key:
        node.right = right_rotate(node.right)
        return left_rotate(node)

    return node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tracing &lt;code&gt;[5, 3, 4]&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Insert &lt;code&gt;5&lt;/code&gt;. Single node, balance factor &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Insert &lt;code&gt;3&lt;/code&gt;. Goes left of &lt;code&gt;5&lt;/code&gt;. Node &lt;code&gt;5&lt;/code&gt;'s balance factor is &lt;code&gt;+1&lt;/code&gt;. Within the rule.&lt;/li&gt;
&lt;li&gt;Insert &lt;code&gt;4&lt;/code&gt;. &lt;code&gt;4 &amp;lt; 5&lt;/code&gt;, go left. &lt;code&gt;4 &amp;gt; 3&lt;/code&gt;, go right of &lt;code&gt;3&lt;/code&gt;. Node &lt;code&gt;4&lt;/code&gt; lands as &lt;code&gt;3&lt;/code&gt;'s right child.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Walking back up: node &lt;code&gt;4&lt;/code&gt; is balance factor &lt;code&gt;0&lt;/code&gt;, node &lt;code&gt;3&lt;/code&gt; is &lt;code&gt;-1&lt;/code&gt;, node &lt;code&gt;5&lt;/code&gt; is &lt;code&gt;+2&lt;/code&gt;. Rule broken at node &lt;code&gt;5&lt;/code&gt;, with its left child right-heavy. That's LR.&lt;/p&gt;
&lt;p&gt;First rotation: left rotation on node &lt;code&gt;3&lt;/code&gt;. Node &lt;code&gt;4&lt;/code&gt; moves up, node &lt;code&gt;3&lt;/code&gt; becomes &lt;code&gt;4&lt;/code&gt;'s left child. Node &lt;code&gt;5&lt;/code&gt; still has balance factor &lt;code&gt;+2&lt;/code&gt;, but its left subtree is now left-heavy. That's LL.&lt;/p&gt;
&lt;p&gt;Second rotation: right rotation on node &lt;code&gt;5&lt;/code&gt;. Node &lt;code&gt;4&lt;/code&gt; becomes the root, node &lt;code&gt;3&lt;/code&gt; is &lt;code&gt;4&lt;/code&gt;'s left child, node &lt;code&gt;5&lt;/code&gt; is &lt;code&gt;4&lt;/code&gt;'s right child. All balance factors back to &lt;code&gt;0&lt;/code&gt;. Height is 1.&lt;/p&gt;
&lt;p&gt;The interviewer asked what a single right rotation would have done to the original tree. Sketched it out: node &lt;code&gt;3&lt;/code&gt; becomes the root, node &lt;code&gt;5&lt;/code&gt; is &lt;code&gt;3&lt;/code&gt;'s right child, but node &lt;code&gt;4&lt;/code&gt; ends up as &lt;code&gt;3&lt;/code&gt;'s right child too, with node &lt;code&gt;5&lt;/code&gt; displaced. The tree depth does not decrease. That's why the double rotation exists.&lt;/p&gt;
&lt;h3 id=&quot;why-this-works-the-height-theory-behind-it-&quot;&gt;Why this works (the height theory behind it)&lt;/h3&gt;
&lt;p&gt;The choice of &lt;code&gt;±1&lt;/code&gt; as the balance factor cap is not arbitrary. It's the tightest constraint that's still cheap to maintain locally.&lt;/p&gt;
&lt;p&gt;An AVL tree with &lt;code&gt;n&lt;/code&gt; nodes has height at most about &lt;code&gt;1.44 * log2(n)&lt;/code&gt;. For a million nodes that's roughly 29 levels. A perfectly balanced tree would be 20 levels. The constant factor is small enough that you get the lookup speed of a balanced tree without paying the cost of full rebalancing on every insert.&lt;/p&gt;
&lt;p&gt;Red-Black trees relax the constraint to allow heights up to about &lt;code&gt;2 log(n)&lt;/code&gt;. That's roughly 40 levels for a million nodes. In exchange, at most 2 rotations per insertion or deletion. Looser balance, fewer rotations, faster writes.&lt;/p&gt;
&lt;p&gt;When Adelson-Velsky and Landis named the AVL tree in 1962, the workload they had in mind was read-heavy. Lookups dominated. The strict balance was worth the rebalancing cost because every lookup was faster. Modern systems insert and delete more than they search-only, which is why Java's &lt;code&gt;TreeMap&lt;/code&gt;, C++'s &lt;code&gt;std::map&lt;/code&gt;, and Linux's &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.kernel.org/scheduler/sched-design-CFS.html&quot;&gt;CFS process scheduler&lt;/a&gt; all picked Red-Black instead. Different workload, different tradeoff. Neither tree is &quot;better.&quot;&lt;/p&gt;
&lt;p&gt;That's the answer the OS interviewer was looking for. AVL is read-optimised. Red-Black is tuned for mixed workloads. Picking between them is a workload question, not a theory question.&lt;/p&gt;
&lt;h3 id=&quot;what-i-d-tell-someone-in-the-same-spot&quot;&gt;What I'd tell someone in the same spot&lt;/h3&gt;
&lt;p&gt;Three things to drill before either interview round:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Master the two atomic moves. Write &lt;code&gt;left_rotate&lt;/code&gt; and &lt;code&gt;right_rotate&lt;/code&gt; from memory, with the height updates in the right order. Three pointer reassignments, two height updates. Everything else builds on these.&lt;/li&gt;
&lt;li&gt;Trace deletion at least three times. Pick at least one trace where the rebalance propagates two levels up. That's the answer to every &quot;what's the tradeoff of stricter balance&quot; question.&lt;/li&gt;
&lt;li&gt;Sketch what a wrong rotation does on an LR case. Once you've drawn the broken tree, the LR / RL trap stops being a memory test.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Codeintuition's &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/courses/binary-search-tree&quot;&gt;Binary Search Tree course&lt;/a&gt; is structured this way. The balance invariant comes before the rotation code, and the rotation code comes before the four-case table. Doing it in that order is what made the table feel mechanical instead of memorised.&lt;/p&gt;
&lt;p&gt;I originally posted this on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/blogs/avl-tree-explained&quot;&gt;my own blog&lt;/a&gt;, a slightly longer version with the worked example for the original &lt;code&gt;[3, 2, 1, 4, 5]&lt;/code&gt; insertion sequence and the AVL vs Red-Black tradeoff matrix.&lt;/p&gt;
&lt;p&gt;What's the AVL or rotation question that finally made the rule click for you, where the textbook explanation had not?&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18486/avl-trees-explained-the-rotation-patterns-most-people-memorise-wrong</guid>
<pubDate>Wed, 20 May 2026 19:57:13 +0000</pubDate>
</item>
<item>
<title>Stop Writing Trading Scripts. Start Building Senses: How I Forged an Autonomous AI trading bot</title>
<link>https://coderlegion.com/14259/stop-writing-trading-scripts-start-building-senses-how-forged-an-autonomous-trading-bot</link>
<description>&lt;h2 id=&quot;forging-herakles-octobot-how-i-built-an-autonomous-ai-crypto-trader-that-actually-understands-risk-&quot;&gt;Forging Herakles-octobot: How I Built an Autonomous AI Crypto Trader (That Actually Understands Risk)&lt;/h2&gt;
&lt;p&gt;The crypto markets are a dark, unforgiving place. It is a realm of endless, 24/7 chaos where traditional algorithmic trading bots march blindly to their slaughter. You write a Python script, hook up some moving average crossovers, and watch your portfolio slowly bleed out like a foot soldier caught in the vanguard. &lt;/p&gt;
&lt;p&gt;Traditional bots are rigid. They don't understand &lt;em&gt;context&lt;/em&gt;. They don't know that a falling Taker Buy/Sell ratio matters more right now than a lagging MACD, or that Extreme Fear on Reddit combined with an oversold StochRSI is a contrarian powder keg waiting to ignite.&lt;/p&gt;
&lt;p&gt;I needed to forge something stronger. Going from an aerospace engineering dropout to a telecom engineer, and ultimately finding my footing as a solo software developer here in Chicago, you learn a fundamental truth about complex, high-stakes systems: surviving the chaos requires dynamic reasoning, not just hardcoded if-statements.&lt;/p&gt;
&lt;p&gt;So, I built an autonomous trading engine. I named it Herakles. I handed the cognitive heavy-lifting to Claude, gave it a $10k paper-trading portfolio to start, and fed it a firehose of real-time market data. &lt;/p&gt;
&lt;p&gt;Here is a technical deep dive into how you force an LLM to stop acting like a helpful chatbot and start acting like a ruthless, calculated execution engine.&lt;/p&gt;
&lt;h4 id=&quot;the-brain-one-prompt-to-bind-them&quot;&gt;The Brain: One Prompt to Bind Them&lt;/h4&gt;
&lt;p&gt;You can't just ask an LLM, &quot;Should I buy Bitcoin?&quot; That is like asking a goblin to guard your gold—it will hallucinate, hedge, and give you a bulleted list of useless financial disclaimers. You have to box it in. You have to build an ironclad cognitive framework.&lt;/p&gt;
&lt;p&gt;My system prompt doesn't ask for opinions. It dictates a strict &lt;strong&gt;Signal Hierarchy&lt;/strong&gt; for 1-hour directional predictions. I force the model to weigh inputs in a very specific order.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;SIGNAL HIERARCHY — what to trust for 1H predictions:

LEADING (these move price in 15-45 min — weight heavily):
  • Taker buy/sell ratio: &amp;gt;1.1=buy pressure, &amp;lt;0.9=sell pressure
  • Volume Delta (CVD): rising=buyers dominating, falling=sellers dominating
  • Orderbook imbalance: immediate supply/demand pressure at current price

CONFIRMING (validate the directional thesis):
  • Multi-TF trend alignment: 1H (45%) + 4H (35%) + 1D (20%). All aligned = high conviction
  • VWAP position: above=bullish institutional bias, below=bearish. Distance amplifies
  • RSI + StochRSI — trending: extremes = strength. Ranging: extremes = reversal

REVERSAL (these override trend signals when present):
  • RSI Divergence on 4H — strongest reversal signal in crypto
  • Extreme funding rate (&amp;gt;0.05%) — crowded trade will snap back

SHORT TRADE RULE:
Crypto has structural long bias. To predict DOWN, require at least 2 of:
taker ratio &amp;lt;0.85, CVD falling, bearish divergence, price below VWAP with declining momentum.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the LLM wants to short a structurally long market like crypto, I require it to provide at least two heavy bearish indicators. It must prove its case mathematically before the backend allows the API call to fire.&lt;/p&gt;
&lt;h4 id=&quot;the-nervous-system-the-all-seeing-eye&quot;&gt;The Nervous System: The All-Seeing Eye&lt;/h4&gt;
&lt;p&gt;An LLM is only as good as its context window. To make this work, the backend acts as an all-seeing eye, aggregating an absurd amount of data every hour. We aren't just looking at Binance price action. The bot ingests 25 data points across 3 timeframes, plus real-time derivatives flow.&lt;/p&gt;
&lt;p&gt;Latency is the enemy. If you are trading 1-hour momentum, you need sub-second aggregation. Here is a snapshot of the internal routing matrix the script uses to build the context payload:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;[
  {&quot;Source&quot;: &quot;Binance MTF (1H/4H/1D)&quot;, &quot;Type&quot;: &quot;technical&quot;, &quot;Latency&quot;: &quot;0ms&quot;, &quot;Contribution&quot;: &quot;95%&quot;},
  {&quot;Source&quot;: &quot;Taker Buy/Sell Ratio&quot;, &quot;Type&quot;: &quot;derivatives&quot;, &quot;Latency&quot;: &quot;0ms&quot;, &quot;Contribution&quot;: &quot;90%&quot;},
  {&quot;Source&quot;: &quot;Orderbook 3-Exchange&quot;, &quot;Type&quot;: &quot;orderbook&quot;, &quot;Latency&quot;: &quot;0ms&quot;, &quot;Contribution&quot;: &quot;80%&quot;},
  {&quot;Source&quot;: &quot;VWAP&quot;, &quot;Type&quot;: &quot;technical&quot;, &quot;Latency&quot;: &quot;0ms&quot;, &quot;Contribution&quot;: &quot;80%&quot;},
  {&quot;Source&quot;: &quot;ClankApp Whales&quot;, &quot;Type&quot;: &quot;onchain&quot;, &quot;Latency&quot;: &quot;0ms&quot;, &quot;Contribution&quot;: &quot;70%&quot;},
  {&quot;Source&quot;: &quot;Fear &amp;amp; Greed Index&quot;, &quot;Type&quot;: &quot;sentiment&quot;, &quot;Latency&quot;: &quot;0ms&quot;, &quot;Contribution&quot;: &quot;70%&quot;}
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-secret-weapon-guarding-the-hoard&quot;&gt;The Secret Weapon: Guarding the Hoard&lt;/h4&gt;
&lt;p&gt;Here is where 99% of experimental AI trading projects fail: they treat every trade in a vacuum. &lt;/p&gt;
&lt;p&gt;Herakles is fully aware of its own wallet. I inject the &lt;code&gt;PORTFOLIO STATE&lt;/code&gt; directly into the prompt alongside the market data. It knows exactly how much capital it has, its current win rate, and the exact P&amp;amp;L of open positions. It guards the hoard.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;=== PORTFOLIO AWARENESS ===
Wallet Summary:
• Total Portfolio Value: $11,367.55 USDT
• Available Balance: $8,764.57 USDT (77.1% cash)

OPEN POSITIONS:
#1 BTC/USDT 
  ├─ Size: 0.03657800 BTC ($2,602.97 / 22.9% of portfolio)
  └─ P&amp;amp;L:  $+97.40 (+3.89%)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I gave it strict exposure rules. If a position is profitable &amp;gt;20%, it must lock in gains. If the portfolio is &amp;gt;70% exposed, it has to minimize new entries. It is programmed for survival first, profit second.&lt;/p&gt;
&lt;h4 id=&quot;the-crucible-holding-the-line&quot;&gt;The Crucible: Holding the Line&lt;/h4&gt;
&lt;p&gt;Let's look at a live execution. The other day, BTC was sitting at $71,168. The macro trend was up. Traditional bots would have likely sounded the horns and slammed the &quot;BUY&quot; button because the price was above the VWAP and the daily MACD was glowing green.&lt;/p&gt;
&lt;p&gt;But Herakles saw something else in the shadows. Look at the strict JSON it returned:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;step_analysis&quot;: {
    &quot;flow&quot;: &quot;BEARISH LEADING SIGNALS: Taker ratio 0.819 (&amp;lt;0.9 = sell pressure), CVD falling (sellers dominating). Orderbook +18.9% provides minor support. Leading indicators conflict with uptrend - suggests pullback in progress.&quot;,
    &quot;momentum&quot;: &quot;MIXED. StochRSI=15 OVERSOLD suggests bounce imminent. CRITICAL: 1H bearish RSI divergence detected - reversal warning but not on 4H.&quot;,
    &quot;context&quot;: &quot;Funding 0.0056% healthy. Fear index 17 = EXTREME FEAR (strong contrarian buy). Whale activity extreme at $2.3B.&quot;
  },
  &quot;prediction&quot;: &quot;up&quot;,
  &quot;confidence&quot;: 54,
  &quot;signal_strength&quot;: &quot;neutral_long&quot;,
  &quot;invalidation_condition&quot;: &quot;Price breaks below S1 $70,496 with increasing volume, or taker ratio drops below 0.75&quot;,
  &quot;suggested_levels&quot;: {
    &quot;stop_loss&quot;: &quot;$70,496 (S1 pivot)&quot;,
    &quot;take_profit_1&quot;: &quot;$71,540 (R1 pivot)&quot;
  },
  &quot;reasoning&quot;: &quot;Uptrend intact but short-term flow is bearish (taker 0.819, CVD falling). However, StochRSI oversold + extreme fear suggests pullback nearing exhaustion. Low confidence due to conflicting leading indicators. Already hold 22.9% BTC at +3.89% profit - no need to add with mixed signals. Hold existing position.&quot;,
  &quot;position_action&quot;: null
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;It decided to do nothing.&lt;/strong&gt; It recognized the bullish macro structure but saw the leading bearish flow (falling CVD, low taker ratio). More importantly, it looked at its portfolio, saw it already had 22% exposure in a profitable position, and decided the risk/reward simply wasn't there to throw more capital into the fray. &lt;/p&gt;
&lt;p&gt;That level of restraint—choosing to HOLD the line in the face of conflicting data—is something most human traders take years and thousands of dollars in liquidations to learn.&lt;/p&gt;
&lt;h4 id=&quot;the-execution-raising-the-shield-wall&quot;&gt;The Execution: Raising the Shield Wall&lt;/h4&gt;
&lt;p&gt;But deciding to &quot;HOLD&quot; doesn't mean the system is sleeping. It means it switches from &lt;em&gt;acquisition&lt;/em&gt; mode to &lt;em&gt;defense&lt;/em&gt; mode. &lt;/p&gt;
&lt;p&gt;While it declined to allocate new capital, the engine autonomously calculated and placed a protective shield wall around the existing position, dispatching OCO (One-Cancels-the-Other) orders to the exchange:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;[
  {
    &quot;Pair&quot;: &quot;BTC/USDT&quot;,
    &quot;Type&quot;: &quot;STOP LOSS&quot;,
    &quot;Price&quot;: 69016.99,
    &quot;Quantity&quot;: 0.018874,
    &quot;TotalValue&quot;: &quot;1302.62 USDT&quot;
  },
  {
    &quot;Pair&quot;: &quot;BTC/USDT&quot;,
    &quot;Type&quot;: &quot;SELL LIMIT&quot;,
    &quot;Price&quot;: 72019.58,
    &quot;Quantity&quot;: 0.018874,
    &quot;TotalValue&quot;: &quot;1359.29 USDT&quot;
  }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice the mechanical precision. It didn't just throw a blind 5% stop loss into the void. It dynamically placed the stop at $69,016—safely tucked beneath the 4H EMA50 ($68,673) and the S1 pivot to avoid getting wicked out by localized volatility. Simultaneously, it set a limit sell above to automatically lock in partial gains if the RSI divergence resolved in a quick liquidity grab.&lt;/p&gt;
&lt;h4 id=&quot;the-road-goes-ever-on&quot;&gt;The Road Goes Ever On&lt;/h4&gt;
&lt;p&gt;We are moving past the era of LLMs as simple code-assistants and entering the era of LLMs as autonomous execution engines. When you give an AI structured rules, massive context, and the ability to execute real API calls, the results are slightly terrifying and deeply inspiring.&lt;/p&gt;
&lt;p&gt;I'm continuing to refine the architecture, tweaking the weights, and hunting for more alpha in the data streams. The journey is far from over.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I'm curious for the devs out there building agentic workflows:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;How are you handling hallucination when the output &lt;em&gt;must&lt;/em&gt; be strict JSON for execution? Do you use a secondary validation script before pushing to the API?&lt;/li&gt;
&lt;li&gt;Are you using smaller, fine-tuned models for speed, or relying on heavyweights for the raw reasoning capabilities?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let's talk about it!&lt;/p&gt;
&lt;p&gt;see some of my other projects at &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/herakles-dev&quot;&gt;https://github.com/herakles-dev&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14259/stop-writing-trading-scripts-start-building-senses-how-forged-an-autonomous-trading-bot</guid>
<pubDate>Wed, 08 Apr 2026 23:30:04 +0000</pubDate>
</item>
<item>
<title>Data Structures and Algorithms on Strings</title>
<link>https://coderlegion.com/13456/data-structures-and-algorithms-on-strings</link>
<description>&lt;p&gt;&lt;strong&gt;Day 1: String Algorithms Overview&lt;/strong&gt; -&lt;/p&gt;
&lt;p&gt;The first day of the ACM Winter School on DSA Strings began with an insightful lecture by Dr. Venkatesh Raman on essential string processing algorithms, including:&lt;/p&gt;
&lt;p&gt;Asymptotic Analysis: Importance of Big-O notation and time/space complexity.&lt;br&gt;
KMP Algorithm: Efficient pattern matching using the prefix function.&lt;br&gt;
Boyer-Moore Algorithm: Focused on the bad character and good suffix rules.&lt;br&gt;
Pattern Matching Techniques: Efficient substring search.&lt;br&gt;
LCS &amp;amp; Edit Distance: Dynamic programming solutions for LCS and applications of edit distance in fields like bioinformatics.&lt;/p&gt;
&lt;p&gt;Sriram Bhyravarapu led a hands-on tutorial where participants implemented the KMP and Boyer-Moore algorithms, practiced pattern matching, and explored dynamic programming with LCS and edit distance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Day 2: Advanced String Algorithms&lt;/strong&gt; -&lt;/p&gt;
&lt;p&gt;The second day focused on advanced topics such as wildcard matching, mismatches, and suffix trees:&lt;/p&gt;
&lt;p&gt;Wildcard Matching: Algorithms for handling * and ? characters in patterns.&lt;br&gt;
Pattern Matching with Mismatches: Techniques for approximate pattern matching.&lt;br&gt;
Suffix Trees: Construction and applications in substring search, longest repeated substring, and lexicographical analysis.&lt;/p&gt;
&lt;p&gt;Sriram Bhyravarapu guided the hands-on implementation of wildcard matching, approximate pattern matching, and suffix trees, giving participants practical experience with these&lt;br&gt;
advanced techniques.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Day 3: Suffix Arrays and Their Applications&lt;/strong&gt; -&lt;/p&gt;
&lt;p&gt;Dr. Sharma Thankachan's lecture focused on the fundamentals of suffix arrays, including their construction (Manber-Myers and Kasai's LCP algorithm) and applications in pattern matching, data compression, and bioinformatics.&lt;/p&gt;
&lt;p&gt;Sriram Bhyravarapu guided participants through hands-on implementation of suffix arrays, LCP arrays, and applications like substring searches, longest repeated substring identification, and Burrows-Wheeler Transform (BWT) for compression tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Day 4: Advanced String Data Structures and Queries&lt;/strong&gt; -&lt;/p&gt;
&lt;p&gt;Dr. Thankachan discussed advanced applications of suffix trees and arrays in text indexing, bioinformatics, and data compression. He also introduced K-th lexicographical substring queries and Range Minimum Queries (RMQ).&lt;/p&gt;
&lt;p&gt;Sriram led coding exercises on K-th queries, RMQs, and their integration with suffix structures. Participants worked on real-world problems involving substring ranking, pattern matching, and optimization.&lt;/p&gt;
&lt;p&gt;The combination of theory and practical coding exercises provided participants with a solid understanding of advanced string algorithms for solving complex real-world problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Day 5: Rank-Select Operations, Wavelet Trees, and Applications&lt;/strong&gt; -&lt;/p&gt;
&lt;p&gt;Lecture by Dr. Chirag Jain:&lt;br&gt;
Introduced rank (count of occurrences) and select (position of K-th occurrence) operations.&lt;br&gt;
Explained wavelet trees for efficient sequence representation and supporting queries.&lt;br&gt;
Discussed real-world applications in text compression, pattern matching, and bioinformatics.&lt;/p&gt;
&lt;p&gt;Tutorial by Sriram Bhyravarapu:&lt;br&gt;
Hands-on implementation of rank-select operations and wavelet tree construction.&lt;br&gt;
Solved practical problems like substring frequency and range queries.&lt;/p&gt;
&lt;p&gt;Key Takeaways:&lt;br&gt;
Gained practical skills in rank-select operations and wavelet trees for computational problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Day 6: Burrows-Wheeler Transform (BWT), FM-Index, and Applications&lt;/strong&gt; -&lt;/p&gt;
&lt;p&gt;Lecture by Dr. Chirag Jain:&lt;br&gt;
Introduced BWT for text compression and FM-Index for efficient substring matching.&lt;br&gt;
Discussed applications in text indexing and genomic data analysis.&lt;/p&gt;
&lt;p&gt;Tutorial by Sriram Bhyravarapu:&lt;br&gt;
Implemented BWT and FM-Index, with exercises in pattern matching and text compression.&lt;/p&gt;
&lt;p&gt;Key Takeaways:&lt;br&gt;
Mastered BWT and FM-Index, essential for text processing and genomic analysis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Day 7: Locality-Sensitive Hashing (LSH), Bloom Filters, and Applications&lt;/strong&gt; -&lt;/p&gt;
&lt;p&gt;Lecture by Dr. Naveen Sivadasan:&lt;br&gt;
Introduced LSH for approximate nearest neighbor searches and Bloom Filters for efficient set membership testing.&lt;br&gt;
Discussed applications in image similarity, web indexing, and network security.&lt;/p&gt;
&lt;p&gt;Tutorial by Sriram Bhyravarapu:&lt;br&gt;
Implemented LSH and Bloom Filters, with exercises on query optimization and false positive rates.&lt;/p&gt;
&lt;p&gt;Key Takeaways:&lt;br&gt;
Learned advanced techniques for efficient data retrieval and set operations using LSH and Bloom Filters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Day 8: Applications and Closing Ceremony&lt;/strong&gt; -&lt;/p&gt;
&lt;p&gt;Session by Dr. Naveen Sivadasan:&lt;br&gt;
Explored real-world applications of LSH and Bloom Filters in data deduplication, genomic data analysis, and recommendation systems.&lt;br&gt;
Highlighted their role in bridging theoretical knowledge with practical industry challenges.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Closing Ceremony:&lt;/strong&gt; -&lt;br&gt;
Reflected on the week-long program, emphasizing the advanced concepts, hands-on skills, and real-world applications covered.&lt;br&gt;
Celebrated participants’ achievements and growth in tackling complex computational problems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Takeaway:&lt;/strong&gt; -&lt;br&gt;
The final day reinforced the practical relevance of advanced algorithms and marked a fitting conclusion to an intensive and enriching learning journey.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/13456/data-structures-and-algorithms-on-strings</guid>
<pubDate>Wed, 25 Mar 2026 04:51:01 +0000</pubDate>
</item>
<item>
<title>Key Math Concepts in Data Structures and Algorithms</title>
<link>https://coderlegion.com/12467/key-math-concepts-in-data-structures-and-algorithms</link>
<description>&lt;p&gt;At the core of data structures and algorithms are fundamental mathematical principles that guide their design and implementation. This article explores key math concepts essential for mastering data structures and algorithms, helping developers write optimized and effective code.&lt;/p&gt;
&lt;h3 id=&quot;number-systems&quot;&gt;Number Systems&lt;/h3&gt;
&lt;p&gt;A &lt;strong&gt;number system&lt;/strong&gt; is a structured way of representing numbers using specific symbols and rules. Different number systems are based on different &lt;strong&gt;base values&lt;/strong&gt; (also called the &lt;em&gt;radix&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;We are most familiar with the &lt;strong&gt;decimal number system&lt;/strong&gt;, which is used in everyday life. It uses ten digits (&lt;strong&gt;0 through 9&lt;/strong&gt;) to represent all numbers. Because it uses ten distinct digits, its base is &lt;strong&gt;10&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Computers, however, operate using only two digits: &lt;strong&gt;0&lt;/strong&gt; and &lt;strong&gt;1&lt;/strong&gt;. Therefore, they use the &lt;strong&gt;binary number system&lt;/strong&gt;, which has a base of &lt;strong&gt;2&lt;/strong&gt;. All data inside a computer is ultimately stored and processed in binary form.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;octal number system&lt;/strong&gt; uses eight digits (&lt;strong&gt;0 to 7&lt;/strong&gt;) so its base is &lt;strong&gt;8&lt;/strong&gt;. It is used as a more compact way to represent binary numbers, since each octal digit corresponds to a group of &lt;strong&gt;three binary digits (bits)&lt;/strong&gt;. Octal is still used in certain areas of computing, such as &lt;strong&gt;file permissions in Unix and Linux systems&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;hexadecimal number system&lt;/strong&gt; uses sixteen symbols: the digits &lt;strong&gt;0–9&lt;/strong&gt; and the letters &lt;strong&gt;A–F&lt;/strong&gt;. Because it has sixteen symbols, its base is &lt;strong&gt;16&lt;/strong&gt;. Each hexadecimal digit corresponds to a group of &lt;strong&gt;four binary digits&lt;/strong&gt;, making it a convenient way to represent memory addresses, machine code, and other low-level data in computers.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The &lt;strong&gt;octal number system&lt;/strong&gt; has a base of 8, which means it uses eight symbols: 0 to 7.&lt;br&gt;
Since &lt;strong&gt;8 = 2³&lt;/strong&gt;, each octal digit can represent exactly &lt;strong&gt;three binary digits (bits)&lt;/strong&gt;. Same goes for hexadecimal number system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can learn how to convert between these number systems from &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.geeksforgeeks.org/digital-logic/number-system-and-base-conversions/&quot;&gt;this detailed article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, let’s talk about the binary number system. We are familiar with representing positive numbers using unsigned binary. But how do we represent negative numbers? For this, we use the &lt;strong&gt;two’s complement method&lt;/strong&gt;, which is the standard way to represent signed binary numbers.&lt;/p&gt;
&lt;h4 id=&quot;two-s-complement-method&quot;&gt;Two's complement method&lt;/h4&gt;
&lt;p&gt;Negative of any decimal number is mathematically found by subtracting that number from 0. The same applies to the binary number system. In the signed binary number system, the &lt;strong&gt;most significant bit&lt;/strong&gt; (the leftmost bit) indicates the sign of the number.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;See the image below if you are not familiar with the most and least significant bits in the binary representation of a number:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1771301336808/840ccc91-fd56-4047-9041-e1c5f1ed2bab.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let’s take the example of decimal 7 whose binary representation is 0111. Its binary number system is stored in 4 bits. Zero can be represented as 0000 (in 4 bits). In 4-bit representation, if a fifth bit is present in the leftmost position, it is discarded due to lack of space. So, we can represent zero as &lt;strong&gt;1&lt;/strong&gt;&lt;em&gt;0000&lt;/em&gt; (the bit 1 will be discarded). Now, &lt;strong&gt;10000&lt;/strong&gt; can be written as the sum of &lt;strong&gt;1111&lt;/strong&gt; and &lt;strong&gt;1&lt;/strong&gt;. We can then proceed as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1771291657535/9ea48090-ea1d-4a5b-9ff1-fb0d1c51638f.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Essentially, &lt;strong&gt;the negative of a binary number will be its complement added to 1&lt;/strong&gt;. This method is called as the &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Two%27s_complement&quot;&gt;two’s complement method&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;bitwise-operators&quot;&gt;Bitwise Operators&lt;/h3&gt;
&lt;p&gt;In the decimal number system, we use arithmetic operators like +, -, *, and / to perform mathematical operations. In computing, numbers are represented in binary, and in addition to arithmetic operators, we also use bitwise operators like &amp;amp;, |, ^, ~, &amp;gt;&amp;gt;, and &amp;lt;&amp;lt; to manipulate individual bits. Let’s discuss each of these operators.&lt;/p&gt;
&lt;h4 id=&quot;and-operator-amp-&quot;&gt;AND Operator (&amp;amp;)&lt;/h4&gt;
&lt;p&gt;This operator performs bit-by-bit AND operation on two values. This means that for each bit in the operands, the resulting bit is set to 1 only if both corresponding bits in the operands are 1; otherwise, it is set to 0.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observation:&lt;/strong&gt; The AND operation of any number with 1 gives the &lt;strong&gt;least significant bit&lt;/strong&gt; (LSB) of that number.&lt;br&gt;
&lt;em&gt;Example&lt;/em&gt;: 5 &amp;amp; 1 results in 1 because 101 &amp;amp; 001 gives its LSB, which is 1.&lt;/p&gt;
&lt;h4 id=&quot;or-operator-&quot;&gt;OR Operator (|)&lt;/h4&gt;
&lt;p&gt;This operator performs a bitwise OR operation between two operands, setting each resulting bit to 1 if at least one of the corresponding bits in the operands is 1; otherwise, it is set to 0.&lt;/p&gt;
&lt;h4 id=&quot;xor-operator-&quot;&gt;XOR Operator (^)&lt;/h4&gt;
&lt;p&gt;XOR stands for &lt;strong&gt;exclusive OR&lt;/strong&gt;. This operator performs a bitwise comparison between two operands, setting each resulting bit to 1 if the corresponding bits in the operands are different; otherwise, it is set to 0.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Observations:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The XOR operation on any number with 1, results in a number where the &lt;strong&gt;least significant bit&lt;/strong&gt; (LSB) is flipped.&lt;br&gt;
&lt;em&gt;Example&lt;/em&gt;: 3 ^ 1 results in 2 because 11 ^ 01 changes the LSB to 0 in this case.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The XOR operation on any number with 0 results in the same number.&lt;br&gt;
&lt;em&gt;Example&lt;/em&gt;: 4 ^ 0 results in 4 because 100 ^ 000 gives 100, which is 4.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The XOR operation on any number with itself results in 0 because there are no differing bits.&lt;br&gt;
&lt;em&gt;Example&lt;/em&gt;: 5 ^ 5 results in 0 because 101 ^ 101 gives 0 as there are no bits that differ.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;complement-operator-&quot;&gt;Complement Operator (~)&lt;/h4&gt;
&lt;p&gt;The complement operator gives the bitwise inverse of a number, flipping each bit from 0 to 1 and from 1 to 0.&lt;/p&gt;
&lt;h4 id=&quot;left-shift-operator-lt-lt-&quot;&gt;Left Shift Operator (&amp;lt;&amp;lt;)&lt;/h4&gt;
&lt;p&gt;The left shift operator is a bitwise operator that shifts the bits of a number to the left by a specified number of positions. &lt;em&gt;Example&lt;/em&gt;: 5 &amp;lt;&amp;lt; 1 gives 10 because shifting the bits of 5 (which is 101 in binary) one position to the left results in 1010, which is 10 in decimal. If we shift twice (5 &amp;lt;&amp;lt; 2), we will get 20 because shifting the bits of 5 (which is 101 in binary) two positions to the left results in 10100, which is 20 in decimal. Therefore, each left shift operation effectively multiplies the number by 2 for each position shifted. In general, if the number is shifted k times, it is multiplied by 2^k.&lt;/p&gt;
&lt;h4 id=&quot;right-shift-operator-gt-gt-&quot;&gt;Right Shift Operator (&amp;gt;&amp;gt;)&lt;/h4&gt;
&lt;p&gt;The right shift operator is a bitwise operator that shifts the bits of a number to the right by a specified number of positions. &lt;em&gt;Example&lt;/em&gt;: 8 &amp;gt;&amp;gt; 1 gives 4 because shifting the bits of 8 (which is 1000 in binary) one position to the right results in 0100, which is 4 in decimal. If we shift twice (8 &amp;gt;&amp;gt; 2), we would get 2 because shifting the bits of 8 (which is 1000 in binary) two positions to the right results in 0010, which is 2 in decimal. Therefore, each right shift operation effectively divides the number by 2 for each position shifted. In general, if the number is shifted k times, it is divided by 2^k.&lt;/p&gt;
&lt;h3 id=&quot;core-number-theory&quot;&gt;Core Number Theory&lt;/h3&gt;
&lt;h4 id=&quot;prime-number&quot;&gt;Prime Number&lt;/h4&gt;
&lt;p&gt;A prime number is a natural number greater than 1 with exactly two positive divisors: 1 and itself. To determine if a number is prime, we typically check divisibility from &lt;strong&gt;2&lt;/strong&gt; up to &lt;strong&gt;n−1&lt;/strong&gt; since it is always divisible by 1 and itself.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int n = 24;
boolean isPrime = true;
for (int i = 2; i &amp;lt; n; i++) {
    if (n % i == 0) {
        isPrime = false;
        break;
    }
}
if (isPrime) {
    System.out.println(&quot;Prime&quot;);
} else {
    System.out.println(&quot;Not prime&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above algorithm makes a maximum of &lt;strong&gt;n - 2&lt;/strong&gt; (2 to n-1) checks. So, its time complexity is &lt;strong&gt;O(N)&lt;/strong&gt;. Let's see if we can optimise it. If we closely examine the factors of any number, they appear in pairs (one smaller and one larger). Mathematically, we can say that for every divisor d &amp;gt; √n (&lt;em&gt;like d = 6&lt;/em&gt;), there exists a corresponding divisor \(\frac {n} {d} &amp;lt; \sqrt n\) (&lt;em&gt;like 4&lt;/em&gt;). Therefore, checking beyond √n is redundant.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/628869289490454c4d8475de/ae7349e0-0bcf-4d9b-9578-24ffed628d95.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;If we look into the above possible pairs of factors of 24, we can see that we're checking divisibility of each pair twice. It is sufficient to check divisibility only up to &lt;strong&gt;√n&lt;/strong&gt; since √24 ≈ 4.89, checking up to 4 is enough. Hence, we can reduce the number of computations from the order of &lt;strong&gt;N&lt;/strong&gt; to &lt;strong&gt;√N&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int n = 24;
boolean isPrime = true;
for (int i = 2; i * i &amp;lt;= n; i++) {
    if (n % i == 0) {
        isPrime = false;
        break;
    }
}
if (isPrime) {
    System.out.println(&quot;Prime&quot;);
} else {
    System.out.println(&quot;Not prime&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The time complexity of this algorithm is now &lt;strong&gt;O(√N)&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;prime-numbers-in-range&quot;&gt;Prime Numbers in Range&lt;/h4&gt;
&lt;p&gt;Let's say we need to find prime numbers between two certain numbers. The simplest idea would be to check if &lt;strong&gt;each of the numbers&lt;/strong&gt; between them is &lt;strong&gt;prime&lt;/strong&gt; or not.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Main {
    public static void main(String[] args) {
        int n = 50;
        displayAllPrimes(n);
    }
    
    public static void displayAllPrimes(int n){
        for (int i = 2; i &amp;lt;= n; i++) {
            if (isPrime(i)) {
                System.out.print(i + &quot; &quot;);
            }
        }
    }
    
    public static boolean isPrime(int n){
        if (n &amp;lt;= 1) return false;
        for (int i = 2; i * i &amp;lt;= n; i++) {
            if (n % i == 0) return false;
        }
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above program, the &lt;code&gt;isPrime()&lt;/code&gt; method takes &lt;strong&gt;O(√N)&lt;/strong&gt; to check whether a certain number is prime. The &lt;code&gt;displayAllPrimes()&lt;/code&gt; method calls the &lt;code&gt;isPrime()&lt;/code&gt; method approximately &lt;strong&gt;N times&lt;/strong&gt;. So, the time complexity of the entire algorithm is &lt;strong&gt;O(N√N)&lt;/strong&gt;. If we carefully observe the numbers we're checking for prime, we'll notice that there are a number of redundant checks. For example, if we already know that 2 is a prime number then its multiples won't be prime.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/628869289490454c4d8475de/f43c11cb-83c2-478f-b55b-3871011ccbd8.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Similarly for the rest of the numbers, the below image shows the multiples of smaller prime numbers we've checked earlier.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/628869289490454c4d8475de/1b7fa69e-cee6-478c-9a2d-72a868e04a6d.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The unmarked numbers turn out to be the &lt;strong&gt;prime numbers&lt;/strong&gt; we're finding in that &lt;strong&gt;range of 2 to 50&lt;/strong&gt;. This is known as the &lt;strong&gt;Sieve of Eratosthenes&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Sieve {
    public static void main(String[] args) {
        int n = 50;
        boolean[] isComposites = new boolean[n+1]; // size is n+1 to include n 
        sieve(n, isComposites);
    }

    public static void sieve(int n, boolean[] isComposite){
        for (int i = 2; i * i &amp;lt;= n; i++) {
            if (!isComposite[i]) {
                for (int j = 2 * i; j &amp;lt;= n; j += i) {
                    isComposite[j] = true;
                }
            }
        }

        for (int i = 2; i &amp;lt;= n; i++) {
            if (!isComposite[i]) {
                System.out.print(i + &quot; &quot;);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the program above, we check for multiples up to &lt;strong&gt;√N&lt;/strong&gt; because large numbers can be factored using smaller numbers' multiples. The number of times the inner loop (the loop responsible for marking the composite numbers) runs for each prime number &lt;strong&gt;P&lt;/strong&gt; is \(\frac {N} {P}\). Therefore, total number of times the inner loop runs will be:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/628869289490454c4d8475de/3754f281-3094-4dc8-87e1-3ca516c17243.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Hence, the time complexity of this algorithm is &lt;strong&gt;O(N loglog(N))&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;newton-raphson-method&quot;&gt;Newton Raphson Method&lt;/h4&gt;
&lt;p&gt;The Newton Raphson method is an iterative method of finding better approximations to the roots of a real-valued function. The best linear approximation to any differential function near the point \(x = x_i\) will be its tangent with the equation:&lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=14937683237160031269&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;where x_i is the ith iterative approximation of root&lt;/p&gt;
&lt;p&gt;The root of the tangent equation will be the next iterative approximation x_{i+1} when it intercepts the x-axis (y = 0).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/628869289490454c4d8475de/4a8807a9-24dc-421e-807a-48137df931df.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, if we want to find the square root of any number (say &lt;strong&gt;n&lt;/strong&gt;), we can take f(x) = x² - n. The root of that equation will give us the closest approximation of &lt;strong&gt;√n&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/628869289490454c4d8475de/28ed9672-f7a3-4498-96c2-71be6864d2ec.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;You can view &lt;a rel=&quot;nofollow&quot; href=&quot;https://upload.wikimedia.org/wikipedia/commons/e/e0/NewtonIteration_Ani.gif&quot;&gt;this animated image from Wikipedia&lt;/a&gt;, which visually demonstrates all the iteration of approximated roots of f(x).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's try to write a program for finding the approximate square root of any number.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class NewtonRaphsonSqrt {
    public static void main(String[] args) {
        System.out.println(sqrt(43));
    }

    public static double sqrt(double n) {
        double x = n;
        double root;
        while (true) {
            root = 0.5 * (x + (n/x));

            if (Math.abs(root - x) &amp;lt; 0.5) { // Error threshold, can be adjusted for more precision
                break;
            }
            x = root;
        }
        return root;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reducing the error threshold, currently set at 0.5, will result in a more accurate approximation of the square root.&lt;/p&gt;
&lt;h4 id=&quot;modular-arithmetic&quot;&gt;Modular Arithmetic&lt;/h4&gt;
&lt;p&gt;It is a system of arithmetic operations for integers where the numbers &quot;&lt;em&gt;wrap around&lt;/em&gt;&quot; when exceeding a certain number called as the &lt;strong&gt;modulus&lt;/strong&gt;. For a given integer m ≥ 1, it is called a modulus if the difference of two integers &lt;strong&gt;a&lt;/strong&gt; and &lt;strong&gt;b&lt;/strong&gt; (&lt;em&gt;a - b&lt;/em&gt;) is an integer multiple of &lt;strong&gt;m&lt;/strong&gt; i.e. a - b = k*m (where k is an integer). The integers &lt;strong&gt;a&lt;/strong&gt; and &lt;strong&gt;b&lt;/strong&gt; are said to be congruent modulo m and is mathematically denoted by &lt;strong&gt;a ≡ b (mod m)&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The parentheses mean that (mod &lt;em&gt;m&lt;/em&gt;) applies to the entire equation, not just to the right-hand side (here, &lt;em&gt;b&lt;/em&gt;). In particular, &lt;strong&gt;(&lt;em&gt;a&lt;/em&gt; mod &lt;em&gt;m&lt;/em&gt;) = (&lt;em&gt;b&lt;/em&gt; mod &lt;em&gt;m&lt;/em&gt;)&lt;/strong&gt; is equivalent to &lt;strong&gt;&lt;em&gt;a&lt;/em&gt; ≡ &lt;em&gt;b&lt;/em&gt; (mod &lt;em&gt;m&lt;/em&gt;)&lt;/strong&gt;, and this explains why &quot;=&quot; is often used instead of &quot;≡&quot; in this context.&lt;/p&gt;
&lt;p&gt;The congruence relation is written as *&lt;em&gt;a = b + k\&lt;/em&gt;m**.&lt;/p&gt;
&lt;p&gt;Source: &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Modular_arithmetic#Congruence&quot;&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In programming, we don’t write congruences directly. Instead, we use the &lt;code&gt;%&lt;/code&gt; operator, which computes the remainder. The &lt;code&gt;%&lt;/code&gt; operator is the practical realization of the congruence relation. For example:  ≡  (mod m) ⇔ (a % m) == (b % m) in code.&lt;/p&gt;
&lt;p&gt;The range of (&lt;strong&gt;a % m&lt;/strong&gt;) is always in the range (&lt;strong&gt;\[0, m-1\]&lt;/strong&gt;) when (m &amp;gt; 0). This ensures the remainder is non-negative, which is crucial for consistent modular arithmetic in programming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Properties:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Addition Property:&lt;/strong&gt; When adding two numbers, their remainders modulo (m) add up, but if the sum exceeds (m), subtracting (m) (i.e., taking modulo again) brings it back within the valid range.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subtraction Property:&lt;/strong&gt; Subtraction can lead to negative results, so adding (m) before taking modulo ensures the result stays within (&lt;strong&gt;\[0, m-1\]&lt;/strong&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multiplication Property:&lt;/strong&gt; Multiplying the remainders and then taking modulo keeps the product within the modular system, preserving equivalence.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Division Property:&lt;/strong&gt; Division is not straightforward in modular arithmetic. For division by (b) modulo (m) to be valid, (b) must have a modular inverse modulo (m), which exists only if gcd(b, m) = 1.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=16810238809413296304&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Modular multiplicative inverse of an integer b is the solution (which exists only when b and m are coprime) of the equation: ax ≡ 1 (mod m).&lt;/p&gt;
&lt;h4 id=&quot;b-zout-s-identity&quot;&gt;&lt;strong&gt;Bézout's Identity&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;It is a theorem which related two arbitrary integers with their greatest common divisor (GCD). Mathematically,&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/uploads/covers/628869289490454c4d8475de/cbd13049-1d9e-49c5-95be-ae193c53ec55.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;where, x and y are integers.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;gcd(a, b) = d&lt;/code&gt;, then every integer linear combination &lt;code&gt;ax + by&lt;/code&gt; is a multiple of d.&lt;/p&gt;
&lt;p&gt;For example, if a = 3 and b = 16, then 3*(-5) + 16*1 = 1 = gcd(3, 16).&lt;br&gt;
Let's take another example: a = 8 and b = 18. Then, 8*(-2) + 18*1 = 2 = gcd(8, 18).&lt;/p&gt;
&lt;p&gt;The coefficients x and y can be found using the Extended Euclidean algorithm and the gcd can be calculated using the Euclidean algorithm.&lt;/p&gt;
&lt;h4 id=&quot;euclidean-algorithm&quot;&gt;Euclidean Algorithm&lt;/h4&gt;
&lt;p&gt;The Euclidean Algorithm is one of the oldest and most efficient methods to compute the greatest common divisor (GCD) of two integers. Since Bézout’s identity relies on the GCD, understanding this algorithm is essential. It's based on the principle that if&lt;/p&gt;
&lt;p&gt;Effectively, a is reduced by multiples of b until only the remainder is left. Therefore, gcd(a, b) = gcd(b, a % b).&lt;/p&gt;
&lt;p&gt;Let’s compute &lt;strong&gt;gcd(514, 106)&lt;/strong&gt; using the Euclidean Algorithm.&lt;/p&gt;
&lt;p&gt;We start by dividing the larger number by the smaller one and continue replacing the pair with (b, a % b) until the remainder becomes zero:&lt;/p&gt;
&lt;p&gt;514 = 106*4 + 90 → gcd(514, 106) = gcd(106, 90) &lt;br&gt;
106 = 90*1 + 16 → gcd(106, 90) = gcd(90, 16) &lt;br&gt;
90 = 16*5 + 10 → gcd(90, 16) = gcd(16, 10) &lt;br&gt;
16 = 10*1 + 6 → gcd(16,10) = gcd(10, 6) &lt;br&gt;
10 = 6*1 + 4 → gcd(10, 6) = gcd(6, 4) &lt;br&gt;
6 = 4*1 + 2 → gcd(6, 4) = gcd(4, 2) &lt;br&gt;
4 = 2*2 + 0 → gcd(4, 2) = 2 &lt;/p&gt;
&lt;p&gt;Therefore, gcd(514, 106) = 2.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static int gcd(int a, int b) {
    if (a == 0) {
        return b;
    }
    return gcd(b % a, a);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also calculate the LCM of two numbers using their GCD with the formula:&lt;br&gt;
\[\text{LCM}(a, b) = \frac{|a \times b|}{\text{GCD}(a, b)}\]&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static int lcm(int a, int b) {
    return a * b / gcd(a, b);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;extended-euclidean-algorithm&quot;&gt;Extended Euclidean Algorithm&lt;/h4&gt;
&lt;p&gt;This algorithm is an extension of the Euclidean Algorithm which computes the coefficients of the Bézout's identity. We start with the gcd of the two numbers and eventually represent that as a linear combination of those two numbers.&lt;/p&gt;
&lt;p&gt;For example, we have found &lt;strong&gt;gcd(514, 106) = 2&lt;/strong&gt;. We start with the last non-zero remainder step: 6 = 4 + 2 ⇒ 2 = 6 - 4.&lt;br&gt;
Then, from previous steps, we can write &lt;strong&gt;6 = 16 - 10&lt;/strong&gt; and &lt;strong&gt;4 = 10 - 6&lt;/strong&gt;. We'll back-substitute them in the above equation.&lt;br&gt;
∴ 2 = (16-10) - (10 - 6) = 16 - 2*10 + 6 = 16 - 2*10 + (16 - 10) = 2*16 - 3*10&lt;br&gt;
∴ 2 = 2*16 - 3*(90 - 16*5) = 17*16 - 3*90 &lt;br&gt;
∴ 2 = 17*(106 - 90) - 3*90 = 17*106 - 20*90  &lt;br&gt;
∴ 2 = 17*106 - 20*(514 - 4*106) = (-20)*514 + 97*106&lt;/p&gt;
&lt;p&gt;Hence, (-20)*514 + 97*106 = 2 = gcd(514, 106). So, the coefficients (&lt;strong&gt;Bézout's coefficients&lt;/strong&gt;) of Bézout's identity would be &lt;strong&gt;-20&lt;/strong&gt; and &lt;strong&gt;97&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We can utilise the recursion stack to keep track of the steps performed in Euclidean Algorithm. Below is the code for this algorithm:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;static int gcdExtended(int a, int b, int[] xy) {
    if (a == 0) {
        xy[0] = 0; // x
        xy[1] = 1; // y
        return b;
    }

    int[] temp = new int[2];
    int gcd = gcdExtended(b % a, a, temp);

    // Update x and y using results of recursion
    xy[0] = temp[1] - (b / a) * temp[0];
    xy[1] = temp[0];

    return gcd;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we explored fundamental mathematical concepts that are crucial for understanding and implementing data structures and algorithms. We delved into number systems, including binary, octal, and hexadecimal, and discussed how computers represent numbers using the two's complement method for signed integers. We also covered bitwise operators and their applications in manipulating individual bits of data.&lt;/p&gt;
&lt;p&gt;⭐ Check out the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/SaptarshiSarkar12/DSA&quot;&gt;DSA&lt;/a&gt; GitHub repo for more code examples.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/12467/key-math-concepts-in-data-structures-and-algorithms</guid>
<pubDate>Sat, 07 Mar 2026 23:13:34 +0000</pubDate>
</item>
<item>
<title>Proving Programs Mathematically: Understanding Hoare Logic with Python</title>
<link>https://coderlegion.com/12181/proving-programs-mathematically-understanding-hoare-logic-with-python</link>
<description>&lt;h3 id=&quot;introduction&quot;&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Have you ever wondered if a program &lt;em&gt;really does what it’s supposed to&lt;/em&gt; — mathematically? &lt;/p&gt;
&lt;p&gt;Most programmers test code manually, but there’s a &lt;strong&gt;formal way to prove program correctness&lt;/strong&gt;: it’s called &lt;strong&gt;Hoare Logic&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this post, we’ll:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Introduce Hoare Logic and its basic idea&lt;/li&gt;
&lt;li&gt;Show how it applies to Python &lt;code&gt;if/else&lt;/code&gt; statements&lt;/li&gt;
&lt;li&gt;Connect it to &lt;strong&gt;algebraic models of loops&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Give you an intuitive way to “prove your code”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By the end, you’ll see how &lt;strong&gt;logic meets code&lt;/strong&gt;, and how &lt;strong&gt;mathematics can verify programs&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-what-is-hoare-logic-&quot;&gt;&lt;strong&gt;1. What is Hoare Logic?&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Hoare Logic was introduced by &lt;strong&gt;C. A. R. Hoare&lt;/strong&gt; in 1969. It’s a method to reason about program correctness using &lt;strong&gt;Hoare triples&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;[
{P}\ S\ {Q}
]&lt;/p&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(P) = &lt;strong&gt;Precondition&lt;/strong&gt; (what must be true before the code)&lt;/li&gt;
&lt;li&gt;(S) = &lt;strong&gt;Statement&lt;/strong&gt; (the program code)&lt;/li&gt;
&lt;li&gt;(Q) = &lt;strong&gt;Postcondition&lt;/strong&gt; (what will be true after the code, if it terminates)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;x = 5
y = x + 2
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Precondition (P): (x = 5)&lt;/li&gt;
&lt;li&gt;Statement (S): &lt;code&gt;y = x + 2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Postcondition (Q): (y = 7)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we can write the Hoare triple:&lt;/p&gt;
&lt;p&gt;[
{x = 5}\ y = x + 2\ {y = 7}
]&lt;/p&gt;
&lt;p&gt;✅ This triple is &lt;strong&gt;mathematically correct&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-proving-if-else-statements&quot;&gt;&lt;strong&gt;2. Proving &lt;code&gt;if/else&lt;/code&gt; Statements&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Hoare Logic has a rule for conditionals:&lt;/p&gt;
&lt;p&gt;[
\frac{{P \wedge B}\ S_1\ {Q},\ {P \wedge \neg B}\ S_2\ {Q}}{{P}\ \text{if } B \text{ then } S_1 \text{ else } S_2\ {Q}}
]&lt;/p&gt;
&lt;p&gt;In plain words:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the condition (B) is true → (S_1) executes correctly&lt;/li&gt;
&lt;li&gt;If (B) is false → (S_2) executes correctly&lt;/li&gt;
&lt;li&gt;Then the entire &lt;code&gt;if/else&lt;/code&gt; is correct&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Python Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;x = 10
if x &amp;gt; 5:
    y = 1
else:
    y = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Precondition (P): &lt;code&gt;x = 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Condition (B): &lt;code&gt;x &amp;gt; 5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Postcondition (Q): &lt;code&gt;y = 1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(x &amp;gt; 5) → executes &lt;code&gt;y = 1&lt;/code&gt; ✅ Correct&lt;/li&gt;
&lt;li&gt;(x &amp;lt;= 5) → would execute &lt;code&gt;y = 2&lt;/code&gt; (not taken in this case)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hoare triple:&lt;/p&gt;
&lt;p&gt;[
{x = 10}\ \text{if } x &amp;gt; 5\ \text{then } y=1\ \text{else } y=2\ {y=1}
]&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-connecting-hoare-logic-to-algebraic-models&quot;&gt;&lt;strong&gt;3. Connecting Hoare Logic to Algebraic Models&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In my research, I represent conditionals &lt;strong&gt;as algebraic functions&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def f(x):
    if x == 0:
        return 0
    else:
        return 1
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt; → false, &lt;code&gt;1&lt;/code&gt; → true&lt;/li&gt;
&lt;li&gt;Precondition: &lt;code&gt;x ∈ {0,1}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Postcondition: output ∈ {0,1}&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mathematically:&lt;/p&gt;
&lt;p&gt;[
f(x) = \begin{cases}
0 &amp;amp; \text{if } x=0 \
1 &amp;amp; \text{if } x=1
\end{cases}
]&lt;/p&gt;
&lt;p&gt;Notice how this &lt;strong&gt;matches Hoare logic&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Precondition (P): (x \in {0,1})&lt;/li&gt;
&lt;li&gt;Statement (S): &lt;code&gt;if/else&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Postcondition (Q): output ∈ {0,1}&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ Algebraic function = Hoare triple in practice&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-loops-summations-and-hoare-invariants&quot;&gt;&lt;strong&gt;4. Loops: Summations and Hoare Invariants&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Hoare Logic handles loops using &lt;strong&gt;loop invariants&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;[
{I \wedge B}\ S\ {I}
]&lt;/p&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(I) = Loop invariant (true before and after every iteration)&lt;/li&gt;
&lt;li&gt;(B) = Loop condition&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For Python &lt;code&gt;for&lt;/code&gt; loops, I model them as &lt;strong&gt;summations over arithmetic progressions&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;for i in range(0, 5):
    sum += i
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;range(0,5)&lt;/code&gt; → 0,1,2,3,4&lt;/li&gt;
&lt;li&gt;Algebraic model:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;[
\sum_{i=0}^{4} i = 10
]&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Precondition: &lt;code&gt;sum = 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Postcondition: &lt;code&gt;sum = 10&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Again, this is &lt;strong&gt;Hoare Logic applied numerically&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;[
{sum = 0}\ \text{for } i \text{ in range(0,5)}\ {sum = 10}
]&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-why-this-matters&quot;&gt;&lt;strong&gt;5. Why This Matters&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;You can &lt;strong&gt;prove code correctness without running it&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Combine &lt;strong&gt;algebraic models + Hoare Logic&lt;/strong&gt; for &lt;strong&gt;clearer reasoning&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;This makes Python programs &lt;strong&gt;mathematically verifiable&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even simple programs can be formally verified, giving you confidence in correctness and runtime behavior.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-conclusion&quot;&gt;&lt;strong&gt;6. Conclusion&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Hoare Logic provides a &lt;strong&gt;mathematical framework for proving program correctness&lt;/strong&gt;, and algebraic models of conditionals and loops give a &lt;strong&gt;computational perspective&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;By combining both:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;if/else&lt;/code&gt; → algebraic functions = Hoare triples&lt;/li&gt;
&lt;li&gt;&lt;code&gt;for loops&lt;/code&gt; → summations = Hoare invariants&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You get a &lt;strong&gt;powerful, accessible, and mathematically sound way&lt;/strong&gt; to reason about programs.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;references&quot;&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Hoare, C. A. R. (1969). &lt;em&gt;An Axiomatic Basis for Computer Programming&lt;/em&gt;. Communications of the ACM, 12(10), 576–580.&lt;/li&gt;
&lt;li&gt;Jadon, P. (2026). &lt;em&gt;A Theoretical Framework for Conditional Statements: Modeling If/Else as Algebraic Functions&lt;/em&gt;. Zenodo. &lt;a rel=&quot;nofollow&quot; href=&quot;https://doi.org/10.5281/zenodo.18758094&quot;&gt;https://doi.org/10.5281/zenodo.18758094&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Jadon, P. (2026). &lt;em&gt;A Mathematical Framework for Python Loops: Modeling For-Loops as Finite Summations&lt;/em&gt;. Zenodo. &lt;a rel=&quot;nofollow&quot; href=&quot;https://doi.org/10.5281/zenodo.18772270&quot;&gt;https://doi.org/10.5281/zenodo.18772270&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;]&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/12181/proving-programs-mathematically-understanding-hoare-logic-with-python</guid>
<pubDate>Wed, 25 Feb 2026 13:47:45 +0000</pubDate>
</item>
<item>
<title>Understanding Basic Data Structures for Web Development</title>
<link>https://coderlegion.com/11727/understanding-basic-data-structures-for-web-development</link>
<description>&lt;p&gt;In the world of web development, grasping basic data structures is vital. They form the backbone of efficient algorithms and applications. Let's dive into some fundamental data structures: arrays, linked lists, and trees.&lt;/p&gt;
&lt;h3 id=&quot;arrays&quot;&gt;Arrays&lt;/h3&gt;
&lt;p&gt;Arrays are collections of elements stored in contiguous memory locations. They offer fast access to elements, making them ideal for scenarios where you need to access or modify data frequently.&lt;/p&gt;
&lt;h4 id=&quot;use-cases-&quot;&gt;Use cases:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Storing lists:&lt;/strong&gt; Arrays are great for managing items like user profiles or product lists.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data manipulation:&lt;/strong&gt; Many algorithms work seamlessly with arrays for sorting or searching data.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;performance-&quot;&gt;Performance:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Access: O(1)&lt;/li&gt;
&lt;li&gt;Insertion/Deletion: O(n) (in worst case)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;linked-lists&quot;&gt;Linked Lists&lt;/h3&gt;
&lt;p&gt;Linked lists consist of nodes where each node points to the next. This structure allows for dynamic memory allocation, making it easier to manage data that grows or shrinks.&lt;/p&gt;
&lt;h4 id=&quot;use-cases--1&quot;&gt;Use cases:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Dynamic data:&lt;/strong&gt; If you’re building applications like chat systems where messages keep coming and going, a linked list can efficiently manage this dynamic set of data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implementing stacks and queues:&lt;/strong&gt; Both can be easily implemented using linked lists for LIFO (Last In, First Out) and FIFO (First In, First Out) structures respectively.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;performance--1&quot;&gt;Performance:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Access: O(n)&lt;/li&gt;
&lt;li&gt;Insertion/Deletion: O(1) (when you have a pointer to the node)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;trees&quot;&gt;Trees&lt;/h3&gt;
&lt;p&gt;Trees are hierarchical structures made up of nodes. The most common type is the binary tree, where each node has at most two children. Trees excel at representing data that has a relational structure.&lt;/p&gt;
&lt;h4 id=&quot;use-cases--2&quot;&gt;Use cases:&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Hierarchical data representation:&lt;/strong&gt; Think about website navigation or organizational structures.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Search operations:&lt;/strong&gt; Binary search trees allow for efficient searching, inserting, and deleting of data.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;performance--2&quot;&gt;Performance:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Access: O(log n) (in balanced trees)&lt;/li&gt;
&lt;li&gt;Insertion/Deletion: O(log n) (in balanced trees)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Understanding data structures not only enhances your programming skills but also allows you to optimize web applications for better performance. Choosing the right data structure for the right job is crucial in building scalable and efficient applications. As web developers, mastering these basics is essential to solve complex problems effectively.&lt;/p&gt;
&lt;p&gt;Don’t hesitate to experiment with these structures in your next project. Whether you’re managing state in a React application or dealing with database results, being well-versed in these data structures will give you the edge you need to create efficient solutions.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/11727/understanding-basic-data-structures-for-web-development</guid>
<pubDate>Mon, 16 Feb 2026 12:00:00 +0000</pubDate>
</item>
<item>
<title>How Insertion Sort Works: Simplified Explanation</title>
<link>https://coderlegion.com/7939/how-insertion-sort-works-simplified-explanation</link>
<description>&lt;p&gt;Insertion sort is a simple comparison-based sorting algorithm that sorts by repeatedly inserting elements into their correct position. Let's explore how it works in detail.&lt;/p&gt;
&lt;h3 id=&quot;what-is-insertion-sort-&quot;&gt;What is Insertion Sort?&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Insertion_sort&quot;&gt;Insertion Sort&lt;/a&gt; is a &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Comparison_sort&quot;&gt;&lt;strong&gt;comparison sort&lt;/strong&gt;&lt;/a&gt; algorithm. It divides the array into two parts: the sorted part and the unsorted part. It repeatedly inserts each element from the unsorted part into its correct position in the sorted part of the array.&lt;/p&gt;
&lt;h3 id=&quot;how-does-it-work-&quot;&gt;How does it work?&lt;/h3&gt;
&lt;p&gt;Let’s take the following array as an example. We need to sort it in ascending order.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764337762308/1378e868-1eca-43ad-aac4-4aecd3595b78.png&quot; alt=&quot;example array&quot;&gt;&lt;/p&gt;
&lt;p&gt;The insertion sort algorithm uses two loops: one controls &lt;strong&gt;the number of passes&lt;/strong&gt; (or traversals) through the array, and the other &lt;strong&gt;compares adjacent elements&lt;/strong&gt; and performs swap if needed, starting from the first element of the unsorted section. Let variable &lt;code&gt;i&lt;/code&gt; control the &lt;strong&gt;number of passes&lt;/strong&gt; and variable &lt;code&gt;j&lt;/code&gt; control the &lt;strong&gt;comparison of adjacent elements&lt;/strong&gt;. As &lt;code&gt;i&lt;/code&gt; increases, the &lt;strong&gt;sorted section grows&lt;/strong&gt;, and &lt;code&gt;j&lt;/code&gt; helps place each element in its correct position within the sorted section.&lt;/p&gt;
&lt;p&gt;Since we are sorting in ascending order, the sorted part of the array is on the left side.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764338538770/9ccdc74c-055e-4369-8059-7eea26e7d029.png&quot; alt=&quot;image of example array showing the i and j pointers along with indices of each element&quot;&gt;&lt;/p&gt;
&lt;p&gt;Initially, &lt;code&gt;i&lt;/code&gt; should start from &lt;strong&gt;index 0&lt;/strong&gt;. The variable &lt;code&gt;j&lt;/code&gt; takes the value &lt;code&gt;i+1&lt;/code&gt; which in the initial case is &lt;strong&gt;index 1&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;As the sorted region lies behind the initial value of &lt;code&gt;j&lt;/code&gt; in each pass, the algorithm compares the &lt;code&gt;j&lt;/code&gt;th element with the &lt;code&gt;j-1&lt;/code&gt;th element.&lt;br&gt;
It will check: is &lt;strong&gt;9&lt;/strong&gt; less than &lt;strong&gt;3&lt;/strong&gt;? No, it isn’t. So, it will get swapped.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764339937848/973dfbe1-2fce-44bb-bcfc-12d94f9cab51.png&quot; alt=&quot;array after the first swap is shown along with the change of pointer values&quot;&gt;&lt;/p&gt;
&lt;p&gt;After the swap, the value of &lt;code&gt;j&lt;/code&gt; decreases by a unit. As the &lt;code&gt;j-1&lt;/code&gt;th element is undefined (error will be thrown if we try to access that element), so &lt;code&gt;j&lt;/code&gt; should always be greater than &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now, in the second pass, &lt;code&gt;i&lt;/code&gt; increases to &lt;strong&gt;1&lt;/strong&gt;. &lt;code&gt;j&lt;/code&gt; takes the value of &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764344362579/d1475935-3e07-4b3f-ab0b-493084a6a6f7.png&quot; alt=&quot;Image showing array with i and j pointers for the second pass and the sorted portion of it is indicated&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, the algorithm checks: is &lt;strong&gt;9&lt;/strong&gt; less than &lt;strong&gt;6&lt;/strong&gt;? No, it is not. So, they will be swapped.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764344426715/d168a420-6055-4bf7-8c0e-863d59c1e43e.png&quot; alt=&quot;array after the first swap in the second pass is shown along with the change of values of pointer variables&quot;&gt;&lt;/p&gt;
&lt;p&gt;The value of &lt;code&gt;j&lt;/code&gt; changes to &lt;strong&gt;1&lt;/strong&gt;. The algorithm now checks: is &lt;strong&gt;3&lt;/strong&gt; less than &lt;strong&gt;6&lt;/strong&gt;? Yes, it is. So, no swap operation will be performed.&lt;/p&gt;
&lt;p&gt;Now, the third pass starts. The value of &lt;code&gt;i&lt;/code&gt; changes to &lt;strong&gt;2&lt;/strong&gt; and &lt;code&gt;j&lt;/code&gt; becomes &lt;strong&gt;3&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764344819083/a5d4da37-2844-4310-8407-c30a91424206.png&quot; alt=&quot;Image showing array with i and j pointers for the third pass and the sorted portion of it is indicated&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, the algorithm checks: is &lt;strong&gt;9&lt;/strong&gt; less than &lt;strong&gt;10&lt;/strong&gt;? Yes, it’s. So, no swap operation will be performed.&lt;/p&gt;
&lt;p&gt;As we know, the &lt;strong&gt;part of the array&lt;/strong&gt; on the &lt;strong&gt;left side of&lt;/strong&gt; &lt;code&gt;j&lt;/code&gt; is &lt;strong&gt;already sorted&lt;/strong&gt; at the &lt;strong&gt;beginning of each pass&lt;/strong&gt;. Therefore, it's unnecessary to check all the way to the leftmost end of the array. If the desired order is found between adjacent elements during comparison, the &lt;strong&gt;internal loop can exit&lt;/strong&gt; for that pass.&lt;/p&gt;
&lt;p&gt;Now, starting with the fourth pass, the value of &lt;code&gt;i&lt;/code&gt; becomes &lt;strong&gt;3&lt;/strong&gt; and that of &lt;code&gt;j&lt;/code&gt; becomes &lt;strong&gt;4&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764345104581/21dcd638-3f03-4ae3-a670-3663d92f8bf8.png&quot; alt=&quot;Image showing array with i and j pointers for the fourth pass and the sorted portion of it is indicated&quot;&gt;&lt;/p&gt;
&lt;p&gt;The algorithm now checks: is &lt;strong&gt;10&lt;/strong&gt; less than &lt;strong&gt;1&lt;/strong&gt;? No, it isn’t. So, they will be swapped.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764373431440/786949dd-f592-4a02-93e5-45d20bf4ef47.png&quot; alt=&quot;array after the first swap in the fourth pass is shown along with the change of values of pointer variables&quot;&gt;&lt;/p&gt;
&lt;p&gt;The value of &lt;code&gt;j&lt;/code&gt; changes to &lt;strong&gt;3&lt;/strong&gt;. Now, it checks: is &lt;strong&gt;9&lt;/strong&gt; less than &lt;strong&gt;1&lt;/strong&gt;? No, it isn’t. So, they’ll be swapped.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764374049930/ec82e24e-3b27-43be-9dd0-5005b0896a06.png&quot; alt=&quot;array after the second swap in the fourth pass is shown along with the change of values of pointer variables&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;j&lt;/code&gt; is now at &lt;strong&gt;index 2&lt;/strong&gt;. It checks: is &lt;strong&gt;6&lt;/strong&gt; less than &lt;strong&gt;1&lt;/strong&gt;? No, it isn’t. So, they get swapped.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764374470145/cbc9ba6e-2f36-4eb7-b26c-d3fab8933a1d.png&quot; alt=&quot;array after the third swap in the fourth pass is shown along with the change of values of pointer variables&quot;&gt;&lt;/p&gt;
&lt;p&gt;The value of &lt;code&gt;j&lt;/code&gt; decreases to &lt;strong&gt;1&lt;/strong&gt;. Now, it checks: is &lt;strong&gt;3&lt;/strong&gt; less than &lt;strong&gt;1&lt;/strong&gt;? No, it isn’t. So, they get swapped.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764375121372/e69ae861-b54b-4226-9eb1-9b5a6ca39bc6.png&quot; alt=&quot;array after the fourth swap in the fourth pass is shown along with the change of values of pointer variables&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, the internal loop exits. As &lt;code&gt;j&lt;/code&gt; takes the value &lt;code&gt;i+1&lt;/code&gt; and &lt;strong&gt;index 5&lt;/strong&gt; does not exist, so the maximum value of &lt;code&gt;i&lt;/code&gt; should be &lt;strong&gt;3&lt;/strong&gt;. In general, &lt;code&gt;i&lt;/code&gt; must be less than &lt;strong&gt;N - 1&lt;/strong&gt; where &lt;strong&gt;N&lt;/strong&gt; is the length of the array.&lt;/p&gt;
&lt;p&gt;Our array is finally sorted!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1764375491533/2f5ca555-fab2-4b44-82fb-e80e5cd06308.png&quot; alt=&quot;Image of the final sorted array&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;time-complexity&quot;&gt;Time Complexity&lt;/h3&gt;
&lt;p&gt;There are two possible scenarios: the &lt;strong&gt;best case&lt;/strong&gt; and the &lt;strong&gt;worst case&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;worst-case&quot;&gt;Worst Case&lt;/h4&gt;
&lt;p&gt;The worst case occurs when the array is sorted in the reverse order. Let’s try to find out the total number of comparisons made.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;1st pass&lt;/strong&gt;, only &lt;strong&gt;1&lt;/strong&gt; comparison is made.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;2nd pass&lt;/strong&gt;, &lt;strong&gt;2&lt;/strong&gt; comparisons are made.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;…&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the &lt;code&gt;N-1&lt;/code&gt;th pass, &lt;code&gt;N-1&lt;/code&gt; comparisons are made.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, the total number of comparisons made is:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=6340850478049330633&quot; alt=&quot;total number of comparisons' calculation is shown&quot;&gt;&lt;/p&gt;
&lt;p&gt;Hence, the time complexity is &lt;strong&gt;O(N²)&lt;/strong&gt; as constants are cancelled, and less dominating terms are removed.&lt;/p&gt;
&lt;h4 id=&quot;best-case&quot;&gt;Best Case&lt;/h4&gt;
&lt;p&gt;The best case occurs when the &lt;strong&gt;array is already sorted in the desired order&lt;/strong&gt;. In that case, there will be &lt;strong&gt;utmost 1 comparison made in each pass&lt;/strong&gt; because the &lt;strong&gt;internal loop will break&lt;/strong&gt; once it finds that the desired order is followed by the adjacent elements while comparing them. Therefore, total number of comparisons made is &lt;code&gt;N-1&lt;/code&gt; where &lt;code&gt;N&lt;/code&gt; is the size of the array.&lt;/p&gt;
&lt;p&gt;Hence, the time complexity in this scenario is &lt;strong&gt;O(N)&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;space-complexity&quot;&gt;Space Complexity&lt;/h3&gt;
&lt;p&gt;As the insertion sort algorithm is an in-place sorting algorithm, it does not use any auxiliary space. Hence, its space complexity is &lt;strong&gt;O(1)&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;stability&quot;&gt;Stability&lt;/h3&gt;
&lt;p&gt;The insertion sort is a &lt;strong&gt;stable sorting algorithm&lt;/strong&gt; as the original order of &lt;strong&gt;the equal elements&lt;/strong&gt; (or the duplicate elements) is maintained in the final sorted array.&lt;/p&gt;
&lt;h3 id=&quot;uses-of-insertion-sort&quot;&gt;Uses of Insertion Sort&lt;/h3&gt;
&lt;p&gt;Insertion sort is the most efficient sorting algorithm for small arrays because it is &lt;strong&gt;adaptive&lt;/strong&gt;. &lt;strong&gt;Adaptive&lt;/strong&gt; means that for smaller values of &lt;strong&gt;N&lt;/strong&gt;, the number of comparisons made are reduced and number of swaps performed is also less compared to &lt;strong&gt;bubble sort&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It works better when the array is nearly sorted. Hence, it is often used in &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Hybrid_algorithm&quot;&gt;hybrid sorting algorithms&lt;/a&gt; like &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Timsort&quot;&gt;TimSort&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Introsort&quot;&gt;IntroSort&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;example-code&quot;&gt;Example Code&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] arr = {9, 3, 6, 10, 1};
        insertionSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    static void insertionSort(int[] arr) {
        for (int i = 0; i &amp;lt; arr.length - 1; i++) {
            for (int j = i + 1; j &amp;gt; 0; j--) {
                if (arr[j] &amp;lt; arr[j - 1]) {
                    swap(arr, j, j-1);
                } else {
                    break;
                }
            }
        }
    }

    static void swap(int[] arr, int first, int second) {
        int temp = arr[first];
        arr[first] = arr[second];
        arr[second] = temp;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion-2&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Insertion sort is a straightforward and efficient algorithm for sorting small arrays. Because of its adaptive nature and its stability, it performs well on nearly sorted arrays. Understanding insertion sort provides a solid foundation for learning more complex sorting techniques.&lt;/p&gt;
&lt;p&gt;⭐ Check out the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/SaptarshiSarkar12/DSA&quot;&gt;&lt;strong&gt;DSA&lt;/strong&gt;&lt;/a&gt; GitHub repo for more code examples.&lt;/p&gt;
</description>
<category>Algorithm</category>
<guid isPermaLink="true">https://coderlegion.com/7939/how-insertion-sort-works-simplified-explanation</guid>
<pubDate>Sat, 29 Nov 2025 09:40:50 +0000</pubDate>
</item>
<item>
<title>Autocomplete Demystified: A Common Use-Case of Trie</title>
<link>https://coderlegion.com/4933/autocomplete-demystified-a-common-use-case-of-trie</link>
<description>&lt;p&gt;Imagine this: You're typing a friendly message in a WhatsApp group directed to one of your peers, and your fingers,&lt;br&gt;
seemingly possessed by a digital imp, instead of typing 'far too nice', types 'fart oo nice'. Now everyone thinks you're&lt;br&gt;
a creep. Frustrating, right? Fear not! Trie is the unseen sidekick behind your keyboard here to maintain your public&lt;br&gt;
image.&lt;/p&gt;
&lt;h4 id=&quot;how-trie-solves-the-typo-trouble&quot;&gt;How 'Trie' Solves the Typo Trouble&lt;/h4&gt;
&lt;p&gt;Think of Trie as your personal autocorrect wizard. When you type a word, Trie steps in to predict what you're trying to&lt;br&gt;
say. It's like having a smart assistant that finishes your sentences, but for typing!&lt;/p&gt;
&lt;p&gt;Imagine you're typing &quot;eleph.&quot; Trie jumps in and goes, &quot;Ah, you must mean 'elephant'!&quot; It's like magic—no more wrestling&lt;br&gt;
with the keyboard to spell out every word perfectly.&lt;/p&gt;
&lt;h4 id=&quot;so-how-does-it-work-&quot;&gt;So, How Does It Work?&lt;/h4&gt;
&lt;p&gt;I'll do something different here. Instead of starting off with a written explanation, I'll do a visual walkthrough on&lt;br&gt;
how the Trie works (yes, I also happen to be an artist). If the visual explanation doesn't work for you, feel free to&lt;br&gt;
read on!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=7044589258516909791&quot; alt=&quot;trie diagram explanation&quot;&gt;&lt;/p&gt;
&lt;p&gt;Picture Trie as a giant family tree of words. Each letter in a word is like a family member. The journey from the root (&lt;br&gt;
the grandparent) to the leaves (the kids) spells out a complete word.&lt;/p&gt;
&lt;p&gt;Let's say you've typed &quot;eleph.&quot; Trie checks its family tree and quickly realizes that &quot;eleph&quot; fits perfectly into the &quot;&lt;br&gt;
elephant&quot; branch. Voila! It suggests the complete word without missing a beat.&lt;/p&gt;
&lt;h4 id=&quot;a-friendly-go-away-message&quot;&gt;A Friendly Go-Away Message&lt;/h4&gt;
&lt;p&gt;This silent sidekick is also evident in other aspects of your life other than Autocorrect. An example most of us might&lt;br&gt;
relate to: Browser History. So for those clandestine folks who toggle 'Incognito Mode' at those wee hours to remove&lt;br&gt;
traces&lt;br&gt;
of their online presence, think of the 'Trie' and it's selfless contributions next time you do this.&lt;/p&gt;
&lt;p&gt;Feel free to share your feedback especially if something wasn't clearly explained!&lt;/p&gt;
&lt;p&gt;If you are intrigued by the inner workings of Trie, and ready for a deep dive into the digital ocean (don't sue me,&lt;br&gt;
digitalocean), keep reading. The next section is where we unveil the code sorcery behind Trie for our tech-savvy&lt;br&gt;
friends. Also, if you have already read the previous blog titled: 'Unveiling the Trie: A Masterstroke for Efficient&lt;br&gt;
String Searches', you can stop here (but you don't have to :-)).&lt;/p&gt;
&lt;p&gt;Gentlemen, and women, and everyone else, shall we?&lt;/p&gt;
&lt;p&gt;A brief introduction to Trie: it's a data structure (similar to an array, a list, a tuple etc.) that stores data as&lt;br&gt;
a tree where the branch of the tree traces out the path to where the data is, i.e. the leaf nodes/child nodes.&lt;/p&gt;
&lt;p&gt;Here's the link to&lt;br&gt;
the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/TylerMutai/coding-challenges-solutions/tree/main/js-coding-challenges-solutions/trie_dictionary&quot;&gt;GitHub repository with the full implementation.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, let's get hands-on with some JavaScript code!&lt;/p&gt;
&lt;p&gt;Here's our Trie implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Trie Node
function TrieEntry(word) {
  this.word = word;
  const words = [];
  for (let i = 0; i &amp;lt; 26; i++) {
    words.push(null);
  }
  this.words = words;
}

// Main Trie Dictionary
function TrieDictionary() {
  const letterToIndexMapping = {
    &quot;a&quot;: 1,
    &quot;b&quot;: 2,
    &quot;c&quot;: 3,
    &quot;d&quot;: 4,
    &quot;e&quot;: 5,
    &quot;f&quot;: 6,
    &quot;g&quot;: 7,
    &quot;h&quot;: 8,
    &quot;i&quot;: 9,
    &quot;j&quot;: 10,
    &quot;k&quot;: 11,
    &quot;l&quot;: 12,
    &quot;m&quot;: 13,
    &quot;n&quot;: 14,
    &quot;o&quot;: 15,
    &quot;p&quot;: 16,
    &quot;q&quot;: 17,
    &quot;r&quot;: 18,
    &quot;s&quot;: 19,
    &quot;t&quot;: 20,
    &quot;u&quot;: 21,
    &quot;v&quot;: 22,
    &quot;w&quot;: 23,
    &quot;x&quot;: 24,
    &quot;y&quot;: 25,
    &quot;z&quot;: 26,
  }

  this.root = new TrieEntry();

  this.insertWord = (word) =&amp;gt; {
    const wordChars = word.toLowerCase().split(&quot;&quot;);
    let currWord = &quot;&quot;;

    let currSlot = this.root;
    for (const char of wordChars) {
      currWord += char;
      // traverse our root adding each char.
      const idx = letterToIndexMapping[char] - 1;
      const arraySlotForChar = currSlot.words[idx];
      if (arraySlotForChar) {
        currSlot = arraySlotForChar;
      } else {
        currSlot.words[idx] = new TrieEntry(currWord);
        currSlot = currSlot.words[idx];
      }
    }
  }

  this.searchWord = (word) =&amp;gt; {
    const wordChars = word.toLowerCase().split(&quot;&quot;);

    let currSlot = this.root;
    for (const char of wordChars) {
      const idx = letterToIndexMapping[char] - 1;
      const arraySlotForChar = currSlot.words[idx];
      if (!arraySlotForChar) {
        break;
      }
      currSlot = arraySlotForChar;
    }
    return currSlot.word === word;
  }

  this.printDictionary = () =&amp;gt; {
    const printRecursively = (entry) =&amp;gt; {
      if (!entry) return &quot;&quot;;

      for (const en of entry.words) {
        if (en) {
          console.log(&quot;en: &quot;, en);
          console.log(&quot;--------------------&quot;);
        }
        printRecursively(en);
      }
    }

    printRecursively(this.root);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;why-trie-a-performance-benchmark-comparison-between-trie-and-linear-search&quot;&gt;Why Trie? A Performance Benchmark Comparison Between Trie and Linear Search&lt;/h4&gt;
&lt;p&gt;Feel free to run the code directly on your machine if you'd prefer a more hands-on approach.&lt;/p&gt;
&lt;h5 id=&quot;1-dictionary-population-1049938-words-&quot;&gt;1. Dictionary Population (1049938 words)&lt;/h5&gt;
&lt;p&gt;Adding words to our Trie is a breeze. Here's the javascript code sample:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const trieDict = new TrieDictionary();
const wordsDictionary = [&quot;apple&quot;, &quot;banana&quot;, &quot;orange&quot;, /* ...a million more words */];

const start = new Date();
for (const word of wordsDictionary) {
  trieDict.insertWord(word);
}
const end = new Date();

const elapsedTime = (end - start) / 1000;
console.log(`Trie Population Time: ${elapsedTime} seconds`);
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Dictionary population (1049938 words) timestamp start:  1699963909921
Timestamp after population:  1699963911482
Trie Population Time:  1.561
------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a mere 1.561 seconds, our Trie is ready to rock and roll with over a million words. Impressive, isn't it?&lt;/p&gt;
&lt;h5 id=&quot;2-the-great-search-showdown&quot;&gt;2. The Great Search Showdown&lt;/h5&gt;
&lt;p&gt;Now, let's pit Trie against the traditional linear search in a battle for supremacy. Our contenders: Trie vs. Linear&lt;br&gt;
Search!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const wordsToSearch = [&quot;apple&quot;, &quot;programming&quot;, &quot;debugging&quot;, /* ...4,829 more words */];

// Linear Search
const linearSearchStart = new Date();
for (const word of wordsToSearch) {
  // ... linear search logic
}
const linearSearchEnd = new Date();

// Trie Search
const trieSearchStart = new Date();
for (const word of wordsToSearch) {
  trieDict.searchWord(word);
}
const trieSearchEnd = new Date();

// Calculating and Displaying Results
const linearSearchTime = (linearSearchEnd - linearSearchStart) / 1000;
const trieSearchTime = (trieSearchEnd - trieSearchStart) / 1000;

console.log(`Linear Search Time: ${linearSearchTime} seconds`);
console.log(`Trie Search Time: ${trieSearchTime} seconds`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Drumroll, please! The results are staggering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Linear Search Time:&lt;/strong&gt; 8.839 seconds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trie Search Time:&lt;/strong&gt; 0.004 seconds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Yes, you read it right! Trie search takes a mere fraction of a second, showcasing a jaw-dropping 2200X performance&lt;br&gt;
boost!&lt;/p&gt;
&lt;h4 id=&quot;use-cases-trie-s-playgrounds&quot;&gt;Use-Cases: 'Trie's' Playgrounds&lt;/h4&gt;
&lt;h5 id=&quot;1-autocomplete-during-search&quot;&gt;1. Autocomplete During Search&lt;/h5&gt;
&lt;p&gt;Ever wondered how autocomplete in search bars works seamlessly? Trie is the wizard behind the curtain, swiftly&lt;br&gt;
predicting your next word as best as it can.&lt;/p&gt;
&lt;h5 id=&quot;2-spell-checking&quot;&gt;2. Spell Checking&lt;/h5&gt;
&lt;p&gt;Tries are responsible for checking typos and spelling mishaps. It detects errors with the swiftness of a&lt;br&gt;
well-optimized algorithm, making corrections before you finish typing &quot;debug.&quot;&lt;/p&gt;
&lt;h4 id=&quot;wrapping-it-up-trie-and-the-future-of-performance&quot;&gt;Wrapping it Up: Trie and the Future of Performance&lt;/h4&gt;
&lt;p&gt;In the blink of an eye, Trie has transformed the landscape of our search experiences. With its unparalleled speed in&lt;br&gt;
word insertion and instant searches, this data structure emerges as a strategic asset for software optimization.&lt;/p&gt;
&lt;p&gt;As you navigate through the complexities of code, consider the Trie as a reliable companion, streamlining your path&lt;br&gt;
through the intricate web of linguistic data.&lt;/p&gt;
&lt;p&gt;Before we part ways, contemplate this: How might integrating Trie elevate the efficiency of your software projects?&lt;br&gt;
Share your insights in the comments below! Stay tuned for our upcoming tech discussions, where we'll unravel more coding&lt;br&gt;
intricacies.&lt;/p&gt;
&lt;p&gt;Wishing you seamless coding journeys!&lt;/p&gt;
</description>
<category>Full-stack Dev</category>
<guid isPermaLink="true">https://coderlegion.com/4933/autocomplete-demystified-a-common-use-case-of-trie</guid>
<pubDate>Sun, 24 Aug 2025 09:42:32 +0000</pubDate>
</item>
<item>
<title>A practical guide to identifying common algorithm patterns for efficient problem-solving.</title>
<link>https://coderlegion.com/4773/practical-guide-to-identifying-common-algorithm-patterns-for-efficient-problem-solving</link>
<description>&lt;h3 id=&quot;the-developer-s-field-guide-to-algorithm-patterns-and-how-to-stop-guessing-&quot;&gt;The Developer's Field Guide to Algorithm Patterns (and How to Stop Guessing)&lt;/h3&gt;
&lt;p&gt;Have you ever faced a coding problem on platforms like LeetCode or HackerRank and felt paralyzed? You read the prompt, understand what's being asked, but your mind goes blank. &quot;Where do I even start?&quot; you ask yourself. &quot;Is this a loop? Two loops? Recursion?&quot;&lt;/p&gt;
&lt;p&gt;This feeling is incredibly common. We often try to solve algorithm problems as if each one were a unique, uncharted island. But the truth is, the vast majority of problems fit into a limited number of fundamental &lt;strong&gt;patterns&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;What if, instead of tackling a problem from scratch, you could recognize its &quot;family&quot; and already have a good idea of the right strategy and tools to solve it? This article is exactly that: a field guide for identifying the most common algorithm patterns based on keywords and the problem's structure. Let's learn to stop guessing and start recognizing.&lt;/p&gt;
&lt;h4 id=&quot;the-lens-we-ll-use-a-quick-review-of-complexity-big-o-&quot;&gt;The Lens We'll Use: A Quick Review of Complexity (Big O)&lt;/h4&gt;
&lt;p&gt;Before we start pattern hunting, we need to adjust our lens. The goal of choosing the right pattern is almost always to find an efficient solution. To measure this efficiency, we use &lt;strong&gt;Big O Notation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The objective isn't to measure the exact time in seconds, but rather to understand &lt;strong&gt;how the execution time grows as the size of the input (n) increases&lt;/strong&gt;. If you double the size of the list, does the runtime double? Does it quadruple? Or does it barely change?&lt;/p&gt;
&lt;p&gt;Remember the &lt;strong&gt;two golden rules of Big O simplification&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Drop Constant Factors:&lt;/strong&gt; A loop that iterates through &lt;code&gt;n&lt;/code&gt; items and one that iterates through &lt;code&gt;n/2&lt;/code&gt; items have the same linear growth. Both are &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep Only the Dominant Term:&lt;/strong&gt; An &lt;code&gt;O(n²)&lt;/code&gt; operation followed by an &lt;code&gt;O(n)&lt;/code&gt; operation is simplified to &lt;strong&gt;O(n²)&lt;/strong&gt;, because the quadratic term dominates the growth for large &lt;code&gt;n&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With that in mind, let's explore the most common patterns.&lt;/p&gt;
&lt;h4 id=&quot;the-field-guide-identifying-8-essential-algorithm-patterns&quot;&gt;The Field Guide: Identifying 8 Essential Algorithm Patterns&lt;/h4&gt;
&lt;h5 id=&quot;sliding-window&quot;&gt;Sliding Window&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to Think About It:&lt;/strong&gt; The problem involves a linear data structure (array, list, string) and asks for a &lt;strong&gt;contiguous&lt;/strong&gt; sub-section (subarray or substring) that is optimal in some way (longest, shortest, max sum, etc.).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keywords / Telltale Signs:&lt;/strong&gt; &quot;contiguous subarray,&quot; &quot;substring,&quot; &quot;longest/shortest length,&quot; &quot;maximum/minimum sum,&quot; &quot;contains k distinct elements.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Problem:&lt;/strong&gt; &quot;Given an array of integers, find the maximum sum of any contiguous subarray of size k.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Complexity:&lt;/strong&gt; O(n).&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;two-pointers&quot;&gt;Two Pointers&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to Think About It:&lt;/strong&gt; The problem involves a &lt;strong&gt;sorted array&lt;/strong&gt; (or one that can be sorted) and asks to find a &lt;strong&gt;pair or a triplet&lt;/strong&gt; of elements that satisfy a condition. Usually, one pointer starts at the beginning and the other at the end, and they move toward each other.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keywords / Telltale Signs:&lt;/strong&gt; &quot;sorted array,&quot; &quot;find a pair/triplet,&quot; &quot;target sum,&quot; &quot;remove duplicates,&quot; &quot;palindrome.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Problem:&lt;/strong&gt; &quot;In a sorted array, find two numbers that add up to a value X.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Complexity:&lt;/strong&gt; O(n).&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;backtracking&quot;&gt;Backtracking&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to Think About It:&lt;/strong&gt; The problem asks to find &lt;strong&gt;all possible solutions&lt;/strong&gt; or a single solution that satisfies complex constraints by building the answer step-by-step. If a choice leads to a dead end, you &quot;go back&quot; (backtrack) and try another. It's a structured Depth-First Search (DFS).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keywords / Telltale Signs:&lt;/strong&gt; &quot;find all combinations,&quot; &quot;permutations,&quot; &quot;subsets,&quot; &quot;all possible paths,&quot; &quot;puzzle,&quot; &quot;Sudoku,&quot; &quot;maze.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Problem:&lt;/strong&gt; &quot;Given a set of numbers, find all unique subsets.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Complexity:&lt;/strong&gt; Exponential, such as O(2ⁿ) or O(n!).&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;dynamic-programming-dp-&quot;&gt;Dynamic Programming (DP)&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to Think About It:&lt;/strong&gt; It's an &lt;strong&gt;optimization problem&lt;/strong&gt; (find the maximum/minimum) or a &lt;strong&gt;counting problem&lt;/strong&gt; (count the number of ways) where the solution to a larger problem can be built from the solutions of &lt;strong&gt;smaller, overlapping subproblems&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keywords / Telltale Signs:&lt;/strong&gt; &quot;number of ways to...&quot;, &quot;minimum/maximum cost path,&quot; &quot;maximum/minimum value you can get,&quot; &quot;maximum profit.&quot; If you find yourself solving the same subproblem multiple times in a recursive solution, it's a sign to use DP.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Problem:&lt;/strong&gt; &quot;Knapsack Problem: given items with weights and values, what is the maximum value that can fit in a knapsack of capacity W?&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Complexity:&lt;/strong&gt; Polynomial, such as O(n²) or O(n*m).&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;breadth-first-search-bfs-&quot;&gt;Breadth-First Search (BFS)&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to Think About It:&lt;/strong&gt; The problem involves graphs or matrices and asks for the &lt;strong&gt;shortest path&lt;/strong&gt; from one point to another, assuming all steps have the same cost. BFS explores the neighborhood in &quot;layers.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keywords / Telltale Signs:&lt;/strong&gt; &quot;shortest path,&quot; &quot;fewest number of steps,&quot; &quot;levels of a tree,&quot; &quot;unweighted graph,&quot; &quot;maze.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Problem:&lt;/strong&gt; &quot;In a matrix with walls, find the shortest path from the top-left corner to the bottom-right.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Complexity:&lt;/strong&gt; O(V + E) (V = vertices, E = edges).&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;depth-first-search-dfs-&quot;&gt;Depth-First Search (DFS)&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to Think About It:&lt;/strong&gt; The problem involves graphs or matrices and asks to explore paths to their end, check if &lt;strong&gt;a path exists&lt;/strong&gt;, or find connected components. It does not guarantee the shortest path. (Backtracking is a type of DFS).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keywords / Telltale Signs:&lt;/strong&gt; &quot;find a path (any path),&quot; &quot;check if a path exists,&quot; &quot;connected components,&quot; &quot;cycles in a graph,&quot; &quot;count islands.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Problem:&lt;/strong&gt; &quot;Given a matrix of 1s (land) and 0s (water), count the number of islands.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Complexity:&lt;/strong&gt; O(V + E).&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;heap-priority-queue-&quot;&gt;Heap (Priority Queue)&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to Think About It:&lt;/strong&gt; The problem asks to find or keep track of the &lt;strong&gt;&quot;top K&quot;&lt;/strong&gt; elements (the K largest or K smallest) from a collection of data, especially if the collection is very large or a stream.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keywords / Telltale Signs:&lt;/strong&gt; &quot;k-th largest/smallest element,&quot; &quot;top k,&quot; &quot;median of a stream,&quot; &quot;schedule tasks&quot; (based on priority).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Problem:&lt;/strong&gt; &quot;Find the 10 most liked tweets from a real-time feed.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Complexity:&lt;/strong&gt; O(n log k) to find the top K elements.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;binary-search&quot;&gt;Binary Search&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When to Think About It:&lt;/strong&gt; The problem involves finding an item in a &lt;strong&gt;sorted array&lt;/strong&gt;. More advanced uses include optimization problems where you can &quot;guess&quot; an answer and check if it's valid, and the search space for answers is monotonic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keywords / Telltale Signs:&lt;/strong&gt; &quot;sorted array,&quot; &quot;find the index of,&quot; &quot;the smallest/largest value X such that condition P(X) is true.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Problem:&lt;/strong&gt; &quot;Find the first number in a sorted array that is greater than or equal to Y.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typical Complexity:&lt;/strong&gt; O(log n).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;appendix-how-to-identify-complexity-in-your-code-the-quick-guide-&quot;&gt;Appendix: How to Identify Complexity in Your Code (The Quick Guide)&lt;/h4&gt;
&lt;p&gt;To help connect theory to practice, here are the most common code patterns and their complexities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O(1) – Constant Time:&lt;/strong&gt; The code takes the same amount of time regardless of the input size. There are no loops that depend on &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def get_first_element(my_list):
    return my_list[0] # Accessing an index is O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O(log n) – Logarithmic Time:&lt;/strong&gt; The algorithm cuts the problem in half with each iteration. Think &lt;strong&gt;Binary Search&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;The loop in a Binary Search throws away half the list with each step.
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O(n) – Linear Time:&lt;/strong&gt; A &lt;strong&gt;single loop&lt;/strong&gt; that iterates over the input of size &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def find_max(my_list):
    max_val = my_list[0]
    for number in my_list: # The loop goes through each of the 'n' elements
        if number &amp;gt; max_val:
            max_val = number
    return max_val
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O(n log n) – &quot;n-log-n&quot; Time:&lt;/strong&gt; Usually associated with &lt;strong&gt;efficient sorting algorithms&lt;/strong&gt; (Merge Sort, Quick Sort). Python's built-in &lt;code&gt;sorted()&lt;/code&gt; function is a good example.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O(n²) – Quadratic Time:&lt;/strong&gt; A &lt;strong&gt;nested loop&lt;/strong&gt; where both loops depend on the input size &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def find_pairs(my_list):
    pairs = []
    for i in my_list: # Outer loop (n)
        for j in my_list: # Inner loop (n)
            pairs.append((i, j))
    return pairs # Total: n * n = n²
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O(2ⁿ) – Exponential Time:&lt;/strong&gt; Often found in a &lt;strong&gt;recursive function&lt;/strong&gt; that calls itself twice (or more) for an input of &lt;code&gt;n-1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def recursive_fibonacci(n):
    if n &amp;lt;= 1: return n
    # For each n, it generates two new &quot;trees&quot; of calls
    return recursive_fibonacci(n-1) + recursive_fibonacci(n-2)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;conclusion-stop-memorizing-start-recognizing&quot;&gt;Conclusion: Stop Memorizing, Start Recognizing&lt;/h4&gt;
&lt;p&gt;Mastering algorithms isn't about memorizing hundreds of specific solutions. It's about building a &lt;strong&gt;mental toolkit of patterns&lt;/strong&gt;. When you encounter a new problem, instead of searching for a pre-made answer, try to identify its characteristics: is it about a contiguous subarray? Think &lt;em&gt;Sliding Window&lt;/em&gt;. Does it ask for the shortest path? Think &lt;em&gt;BFS&lt;/em&gt;. Does it need all possible combinations? Think &lt;em&gt;Backtracking&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This field guide is your starting point. The more you practice with this pattern-recognition mindset, the faster and more intuitive your problem-solving process will become.&lt;/p&gt;
</description>
<category>Full-stack Dev</category>
<guid isPermaLink="true">https://coderlegion.com/4773/practical-guide-to-identifying-common-algorithm-patterns-for-efficient-problem-solving</guid>
<pubDate>Mon, 18 Aug 2025 06:43:05 +0000</pubDate>
</item>
</channel>
</rss>