<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged dsa</title>
<link>https://coderlegion.com/tag/dsa</link>
<description>Powered by Question2Answer</description>
<item>
<title>When DSA Solved Our AWS Scaling Problem</title>
<link>https://coderlegion.com/33061/when-dsa-solved-our-aws-scaling-problem</link>
<description>&lt;h3 id=&quot;when-dsa-solved-our-aws-scaling-problem&quot;&gt;When DSA Solved Our AWS Scaling Problem&lt;/h3&gt;
&lt;p&gt;In the age of AI, it feels like DSA is slowly disappearing from day-to-day development.&lt;/p&gt;
&lt;p&gt;You can ask an AI to generate a queue, configure an AWS service, or write a retry mechanism in seconds.&lt;/p&gt;
&lt;p&gt;But there is one thing AI cannot remove from engineering:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Constraints.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And when constraints appear, DSA suddenly becomes relevant again.&lt;/p&gt;
&lt;p&gt;At our startup, we often solve problems in a scalable way. But scalability doesn't always mean adding another AWS service. Sometimes, the solution is simply &lt;strong&gt;using the existing components differently&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You usually don't recognize a DSA concept in a real-world system until someone asks:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;Why are we doing it this way?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That question led us to a surprisingly simple solution to an AWS scheduling problem.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem-we-were-running-out-of-schedules&quot;&gt;The Problem: We Were Running Out of Schedules&lt;/h3&gt;
&lt;p&gt;Imagine we have a large number of jobs.&lt;/p&gt;
&lt;p&gt;Each job can fail and needs to be retried on subsequent days:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Parent Job
   │
   ├── Retry 1 → +1 day
   ├── Retry 2 → +2 days
   └── Retry 3 → +3 days
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Initially, this seems straightforward.&lt;/p&gt;
&lt;p&gt;We can simply create an EventBridge schedule for every retry.&lt;/p&gt;
&lt;p&gt;But then scale enters the picture.&lt;/p&gt;
&lt;p&gt;We encountered a situation where, for every &lt;strong&gt;6,000 jobs&lt;/strong&gt;, we needed to create &lt;strong&gt;3 separate EventBridge schedules&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That means:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;6,000 jobs
   ×
3 retry schedules
   =
18,000 schedules
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now imagine increasing the workload.&lt;/p&gt;
&lt;p&gt;What happens if we have &lt;strong&gt;10,000 jobs&lt;/strong&gt;?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;10,000 jobs
    ×
3 retries
    =
30,000 schedules
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And what if this keeps growing?&lt;/p&gt;
&lt;p&gt;We eventually run into EventBridge scheduling limits.&lt;/p&gt;
&lt;p&gt;The obvious solution might be:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Let's add another AWS component.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But our senior asked a different question:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;Can we solve this using a sliding window?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That was the interesting part.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;first-what-is-a-sliding-window-&quot;&gt;First, What Is a Sliding Window?&lt;/h3&gt;
&lt;p&gt;A sliding window is a technique where we don't process the entire dataset at once.&lt;/p&gt;
&lt;p&gt;Instead, we maintain a smaller &lt;strong&gt;active window&lt;/strong&gt; and continuously move it forward.&lt;/p&gt;
&lt;p&gt;For example, imagine these jobs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Day 1   Day 2   Day 3   Day 4   Day 5   Day 6   Day 7
 ├───────┼───────┼───────┼───────┼───────┼───────┼───────┤
         [──────────── Window ────────────]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As time moves forward:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Before:

Day 1   Day 2   Day 3   Day 4   Day 5   Day 6   Day 7
         [──────── Window ────────]


After:

Day 1   Day 2   Day 3   Day 4   Day 5   Day 6   Day 7
                 [──────── Window ────────]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important idea is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We don't need to keep scheduling everything forever.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We only care about jobs relevant to the current time window.&lt;/p&gt;
&lt;p&gt;That observation changes how we think about the EventBridge problem.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-if-we-don-t-schedule-every-retry-&quot;&gt;What If We Don't Schedule Every Retry?&lt;/h3&gt;
&lt;p&gt;This was the key idea.&lt;/p&gt;
&lt;p&gt;Instead of asking:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;When should this retry happen?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;and creating an EventBridge schedule for that future date...&lt;/p&gt;
&lt;p&gt;we ask:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;Which retries are supposed to happen today?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That small change removes the need to schedule every future retry individually.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-original-approach&quot;&gt;The Original Approach&lt;/h3&gt;
&lt;p&gt;Let's say today is &lt;strong&gt;August 18&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A parent job runs today.&lt;/p&gt;
&lt;p&gt;Its retries are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;August 18
   │
   ├── Retry 1 → August 19
   ├── Retry 2 → August 20
   └── Retry 3 → August 21
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The traditional approach is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;              EventBridge
                  │
       ┌──────────┼──────────┐
       ▼          ▼          ▼
    Aug 19     Aug 20     Aug 21
    Retry 1    Retry 2    Retry 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For every parent job, we create future schedules.&lt;/p&gt;
&lt;p&gt;This is where the number of schedules grows rapidly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-sliding-window-approach&quot;&gt;The Sliding Window Approach&lt;/h3&gt;
&lt;p&gt;Now let's change the perspective.&lt;/p&gt;
&lt;p&gt;Instead of scheduling:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Retry 1 → tomorrow
Retry 2 → day after tomorrow
Retry 3 → three days later
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we maintain a &lt;strong&gt;daily execution window&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Every day, we ask:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;Which existing parent jobs have a retry due today?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then we execute those retries.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;                    TODAY
                      │
                      ▼
             ┌─────────────────┐
             │   Sliding       │
             │    Window       │
             └────────┬────────┘
                      │
        ┌─────────────┼─────────────┐
        ▼             ▼             ▼
     Retry 1       Retry 2       Retry 3
      due today     due today     due today
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;EventBridge no longer needs to know about every individual retry.&lt;/p&gt;
&lt;p&gt;It only needs to trigger the &lt;strong&gt;parent job&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;but-what-about-jobs-from-previous-days-&quot;&gt;But What About Jobs From Previous Days?&lt;/h3&gt;
&lt;p&gt;This is where the idea becomes interesting.&lt;/p&gt;
&lt;p&gt;Suppose a parent job ran &lt;strong&gt;3 days ago&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Let's say:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Parent Job
August 15
   │
   ├── Retry 1 → August 16
   ├── Retry 2 → August 17
   └── Retry 3 → August 18
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Today is August 18.&lt;/p&gt;
&lt;p&gt;The third retry is due &lt;strong&gt;today&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Under the traditional approach, we would have created a schedule on August 15 for the retry that should execute on August 18.&lt;/p&gt;
&lt;p&gt;But we don't need to do that anymore.&lt;/p&gt;
&lt;p&gt;Instead, today's sliding window discovers it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;August 15        August 16        August 17        August 18
Parent Job       Retry 1          Retry 2          Retry 3
    │                │                │                │
    └────────────────┴────────────────┴────────────────┘
                                                     ▲
                                                     │
                                                   TODAY
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The system simply asks:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;What retries are due on August 18?&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finds:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Parent Job: August 15
Retry:      3
Due:        August 18
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then it executes it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-trick&quot;&gt;The Trick&lt;/h3&gt;
&lt;p&gt;This is the entire trick:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don't schedule the future retries.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Instead Conceptually:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;                 ┌─────────────────────┐
                 │   Daily Trigger     │
                 └──────────┬──────────┘
                            │
                            ▼
                 ┌─────────────────────┐
                 │  Current Time      │
                 │     Window         │
                 └──────────┬──────────┘
                            │
                            ▼
              ┌──────────────────────────┐
              │ Find retries due today   │
              └────────────┬─────────────┘
                           │
                 ┌─────────┴─────────┐
                 ▼                   ▼
            Retry due             Not due
                 │                   │
                 ▼                   ▼
              Execute              Ignore

&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;why-this-scales-better&quot;&gt;Why This Scales Better&lt;/h3&gt;
&lt;p&gt;The important difference is &lt;strong&gt;what we are scheduling&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Number of schedules grows&lt;br&gt;
with the number of jobs&lt;/p&gt;
&lt;p&gt;Now the number of EventBridge schedules is no longer directly proportional to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Number of Jobs × Number of Retries
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead, EventBridge becomes the mechanism that wakes up the &lt;strong&gt;scheduler/processor&lt;/strong&gt;, while the application determines which work actually needs to happen.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-bigger-lesson&quot;&gt;The Bigger Lesson&lt;/h3&gt;
&lt;p&gt;The interesting part of this problem wasn't really EventBridge.&lt;/p&gt;
&lt;p&gt;It was recognizing that we were using EventBridge to solve a problem that didn't necessarily belong there.&lt;/p&gt;
&lt;p&gt;We initially thought:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;We need to schedule millions of future events.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The better question was:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;Do we actually need to schedule millions of future events?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And the answer was no.&lt;/p&gt;
&lt;p&gt;We only needed to know:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;What needs to happen today?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is where a simple DSA concept like &lt;strong&gt;sliding window&lt;/strong&gt; becomes a practical system-design technique.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;dsa-is-not-dead&quot;&gt;DSA Is Not Dead&lt;/h3&gt;
&lt;p&gt;This is why I don't think DSA is disappearing from software engineering.&lt;br&gt;
Sometimes the answer is a sophisticated distributed system.&lt;/p&gt;
&lt;p&gt;And sometimes...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;it's a sliding window.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The important skill isn't memorizing DSA patterns.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33061/when-dsa-solved-our-aws-scaling-problem</guid>
<pubDate>Fri, 30 Oct 2026 08:39:12 +0000</pubDate>
</item>
<item>
<title>What if your wardrobe could teach you Sorting Algorithms?👗</title>
<link>https://coderlegion.com/28251/what-if-your-wardrobe-could-teach-you-sorting-algorithms</link>
<description>&lt;p&gt;You open your wardrobe.&lt;/p&gt;
&lt;p&gt;T-shirts mixed with jeans.&lt;br&gt;
Dresses hiding behind hoodies.&lt;br&gt;
Everything is everywhere. 😭&lt;/p&gt;
&lt;p&gt;You need to sort it.&lt;/p&gt;
&lt;p&gt;But here's the catch...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;There isn't just one way to organize a messy wardrobe.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And that's exactly what happens in DSA.&lt;/p&gt;
&lt;p&gt;An unsorted array is basically a messy wardrobe.&lt;br&gt;
The goal is the same: &lt;strong&gt;put everything in order.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;But every sorting algorithm has its own strategy.&lt;/p&gt;
&lt;p&gt;Let's meet them. 👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-bubble-sort-compare-the-neighbors&quot;&gt;1. Bubble Sort — Compare the neighbors&lt;/h3&gt;
&lt;p&gt;Imagine checking your clothes &lt;strong&gt;two at a time&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If the two neighboring clothes are in the wrong order:&lt;/p&gt;
&lt;p&gt;🔄 Swap them.&lt;/p&gt;
&lt;p&gt;Then move to the next pair.&lt;/p&gt;
&lt;p&gt;Keep doing this until the largest item gradually reaches the end.&lt;/p&gt;
&lt;p&gt;That's why it's called &lt;strong&gt;Bubble Sort&lt;/strong&gt;—elements &quot;bubble&quot; toward their correct positions.&lt;/p&gt;
&lt;h4 id=&quot;-java&quot;&gt;💻 Java&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class BubbleSort {

    public static void main(String[] args) {

        int[] arr = {5, 3, 8, 4, 2};

        for (int i = 0; i &amp;lt; arr.length - 1; i++) {

            for (int j = 0; j &amp;lt; arr.length - i - 1; j++) {

                if (arr[j] &amp;gt; arr[j + 1]) {

                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }

        for (int num : arr) {
            System.out.print(num + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; O(n²)&lt;br&gt;
&lt;strong&gt;Space:&lt;/strong&gt; O(1)&lt;/p&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Bubble = Compare neighbors + Swap&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-2-selection-sort-find-the-right-one&quot;&gt;🎯 2. Selection Sort — Find the right one&lt;/h3&gt;
&lt;p&gt;Now you change your strategy.&lt;/p&gt;
&lt;p&gt;Instead of repeatedly comparing neighbors, you look through the entire unsorted section.&lt;/p&gt;
&lt;p&gt;You find the &lt;strong&gt;smallest item&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Then...&lt;/p&gt;
&lt;p&gt;📍 Put it in its correct position.&lt;/p&gt;
&lt;p&gt;Repeat.&lt;/p&gt;
&lt;p&gt;It's like saying:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&quot;First, I'll find the smallest shirt. Then the next smallest. Then the next...&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That's &lt;strong&gt;Selection Sort.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;-java-1&quot;&gt;💻 Java&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class SelectionSort {

    public static void main(String[] args) {

        int[] arr = {64, 25, 12, 22, 11};

        for (int i = 0; i &amp;lt; arr.length - 1; i++) {

            int minIndex = i;

            for (int j = i + 1; j &amp;lt; arr.length; j++) {

                if (arr[j] &amp;lt; arr[minIndex]) {
                    minIndex = j;
                }
            }

            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }

        for (int num : arr) {
            System.out.print(num + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; O(n²)&lt;br&gt;
&lt;strong&gt;Space:&lt;/strong&gt; O(1)&lt;/p&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Selection = Find minimum + Place it&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-3-insertion-sort-insert-it-where-it-belongs&quot;&gt;🃏 3. Insertion Sort — Insert it where it belongs&lt;/h3&gt;
&lt;p&gt;Now imagine arranging clothes one by one.&lt;/p&gt;
&lt;p&gt;You already have a few clothes arranged.&lt;/p&gt;
&lt;p&gt;You pick the next one.&lt;/p&gt;
&lt;p&gt;Look at the sorted section.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&quot;Where does this belong?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You make space and insert it there.&lt;/p&gt;
&lt;p&gt;Just like arranging playing cards in your hand. 🃏&lt;/p&gt;
&lt;p&gt;That's &lt;strong&gt;Insertion Sort.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;-java-2&quot;&gt;💻 Java&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class InsertionSort {

    public static void main(String[] args) {

        int[] arr = {5, 3, 4, 1, 2};

        for (int i = 1; i &amp;lt; arr.length; i++) {

            int key = arr[i];
            int j = i - 1;

            while (j &amp;gt;= 0 &amp;amp;&amp;amp; arr[j] &amp;gt; key) {

                arr[j + 1] = arr[j];
                j--;
            }

            arr[j + 1] = key;
        }

        for (int num : arr) {
            System.out.print(num + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Best:&lt;/strong&gt; O(n)&lt;br&gt;
&lt;strong&gt;Worst:&lt;/strong&gt; O(n²)&lt;br&gt;
&lt;strong&gt;Space:&lt;/strong&gt; O(1)&lt;/p&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Insertion = Take + Shift + Insert&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-4-merge-sort-don-t-organize-everything-at-once&quot;&gt;✂️ 4. Merge Sort — Don't organize everything at once&lt;/h2&gt;
&lt;p&gt;You look at the wardrobe.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&quot;There are WAY too many clothes. I'm splitting this.&quot;&lt;/em&gt; 😭&lt;/p&gt;
&lt;p&gt;Divide the clothes into two sections.&lt;/p&gt;
&lt;p&gt;Then divide those sections again.&lt;/p&gt;
&lt;p&gt;Keep dividing until each section is tiny.&lt;/p&gt;
&lt;p&gt;Now sort them.&lt;/p&gt;
&lt;p&gt;Then merge the sorted sections back together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Divide → Sort → Merge&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That's &lt;strong&gt;Merge Sort.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;-java-3&quot;&gt;💻 Java&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class MergeSort {

    static void mergeSort(int[] arr, int left, int right) {

        if (left &amp;lt; right) {

            int mid = left + (right - left) / 2;

            mergeSort(arr, left, mid);
            mergeSort(arr, mid + 1, right);

            merge(arr, left, mid, right);
        }
    }

    static void merge(int[] arr, int left, int mid, int right) {

        int[] temp = new int[right - left + 1];

        int i = left;
        int j = mid + 1;
        int k = 0;

        while (i &amp;lt;= mid &amp;amp;&amp;amp; j &amp;lt;= right) {

            if (arr[i] &amp;lt;= arr[j]) {
                temp[k++] = arr[i++];
            } else {
                temp[k++] = arr[j++];
            }
        }

        while (i &amp;lt;= mid) {
            temp[k++] = arr[i++];
        }

        while (j &amp;lt;= right) {
            temp[k++] = arr[j++];
        }

        for (int x = 0; x &amp;lt; temp.length; x++) {
            arr[left + x] = temp[x];
        }
    }

    public static void main(String[] args) {

        int[] arr = {8, 3, 5, 4, 7, 6, 1, 2};

        mergeSort(arr, 0, arr.length - 1);

        for (int num : arr) {
            System.out.print(num + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; O(n log n)&lt;br&gt;
&lt;strong&gt;Space:&lt;/strong&gt; O(n)&lt;/p&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Merge = Divide + Sort + Merge&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-5-quick-sort-choose-a-pivot&quot;&gt;⚡ 5. Quick Sort — Choose a pivot&lt;/h2&gt;
&lt;p&gt;Now you want a faster strategy.&lt;/p&gt;
&lt;p&gt;You pick one item as your &lt;strong&gt;pivot&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Everything smaller goes to one side.&lt;/p&gt;
&lt;p&gt;Everything larger goes to the other.&lt;/p&gt;
&lt;p&gt;Now you've created smaller problems.&lt;/p&gt;
&lt;p&gt;Repeat the same process.&lt;/p&gt;
&lt;p&gt;That's &lt;strong&gt;Quick Sort.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;-java-4&quot;&gt;💻 Java&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class QuickSort {

    static void quickSort(int[] arr, int low, int high) {

        if (low &amp;lt; high) {

            int pivotIndex = partition(arr, low, high);

            quickSort(arr, low, pivotIndex - 1);
            quickSort(arr, pivotIndex + 1, high);
        }
    }

    static int partition(int[] arr, int low, int high) {

        int pivot = arr[high];

        int i = low - 1;

        for (int j = low; j &amp;lt; high; j++) {

            if (arr[j] &amp;lt; pivot) {

                i++;

                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }

        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;

        return i + 1;
    }

    public static void main(String[] args) {

        int[] arr = {10, 7, 8, 9, 1, 5};

        quickSort(arr, 0, arr.length - 1);

        for (int num : arr) {
            System.out.print(num + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Average:&lt;/strong&gt; O(n log n)&lt;br&gt;
&lt;strong&gt;Worst:&lt;/strong&gt; O(n²)&lt;br&gt;
&lt;strong&gt;Space:&lt;/strong&gt; O(log n) average recursion stack&lt;/p&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Quick = Pivot + Partition&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-6-heap-sort-priority-comes-first&quot;&gt;🌳 6. Heap Sort — Priority comes first&lt;/h2&gt;
&lt;p&gt;Now imagine you want the &lt;strong&gt;highest-priority clothing item&lt;/strong&gt; to always be easy to find.&lt;/p&gt;
&lt;p&gt;You organize the items using a special structure:&lt;/p&gt;
&lt;p&gt;🌳 &lt;strong&gt;Heap&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The largest element can stay at the top of a &lt;strong&gt;Max Heap&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Take it.&lt;/p&gt;
&lt;p&gt;Put it in its final position.&lt;/p&gt;
&lt;p&gt;Restore the heap.&lt;/p&gt;
&lt;p&gt;Repeat.&lt;/p&gt;
&lt;p&gt;That's &lt;strong&gt;Heap Sort.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;-java-5&quot;&gt;💻 Java&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class HeapSort {

    static void heapSort(int[] arr) {

        int n = arr.length;

        // Build Max Heap
        for (int i = n / 2 - 1; i &amp;gt;= 0; i--) {
            heapify(arr, n, i);
        }

        // Move maximum to the end
        for (int i = n - 1; i &amp;gt; 0; i--) {

            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;

            heapify(arr, i, 0);
        }
    }

    static void heapify(int[] arr, int n, int i) {

        int largest = i;

        int left = 2 * i + 1;
        int right = 2 * i + 2;

        if (left &amp;lt; n &amp;amp;&amp;amp; arr[left] &amp;gt; arr[largest]) {
            largest = left;
        }

        if (right &amp;lt; n &amp;amp;&amp;amp; arr[right] &amp;gt; arr[largest]) {
            largest = right;
        }

        if (largest != i) {

            int temp = arr[i];
            arr[i] = arr[largest];
            arr[largest] = temp;

            heapify(arr, n, largest);
        }
    }

    public static void main(String[] args) {

        int[] arr = {12, 11, 13, 5, 6, 7};

        heapSort(arr);

        for (int num : arr) {
            System.out.print(num + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; O(n log n)&lt;br&gt;
&lt;strong&gt;Space:&lt;/strong&gt; O(1)&lt;/p&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Heap = Build heap + Extract repeatedly&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-7-counting-sort-stop-comparing-start-counting-&quot;&gt;🔢 7. Counting Sort — Stop comparing. Start counting.&lt;/h2&gt;
&lt;p&gt;Here's where things get interesting.&lt;/p&gt;
&lt;p&gt;Imagine your clothes can only have sizes:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1, 2, 3, 4, 5&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Instead of comparing every item...&lt;/p&gt;
&lt;p&gt;You simply count how many times each size appears.&lt;/p&gt;
&lt;p&gt;That's the idea behind &lt;strong&gt;Counting Sort.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;-java-6&quot;&gt;💻 Java&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class CountingSort {

    static void countingSort(int[] arr) {

        int max = arr[0];

        for (int num : arr) {
            if (num &amp;gt; max) {
                max = num;
            }
        }

        int[] count = new int[max + 1];

        for (int num : arr) {
            count[num]++;
        }

        int index = 0;

        for (int i = 0; i &amp;lt; count.length; i++) {

            while (count[i] &amp;gt; 0) {

                arr[index] = i;
                index++;

                count[i]--;
            }
        }
    }

    public static void main(String[] args) {

        int[] arr = {4, 2, 2, 8, 3, 3, 1};

        countingSort(arr);

        for (int num : arr) {
            System.out.print(num + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; O(n + k)&lt;br&gt;
&lt;strong&gt;Space:&lt;/strong&gt; O(k)&lt;/p&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Counting = Count frequencies&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;⚠️ Works well when the range of values is reasonably small.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-8-radix-sort-one-digit-at-a-time&quot;&gt;📚 8. Radix Sort — One digit at a time&lt;/h2&gt;
&lt;p&gt;Now imagine numbers are your clothes.&lt;/p&gt;
&lt;p&gt;Instead of comparing the whole number...&lt;/p&gt;
&lt;p&gt;You organize them &lt;strong&gt;digit by digit&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;First:&lt;/p&gt;
&lt;p&gt;➡️ Ones digit&lt;/p&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;p&gt;➡️ Tens digit&lt;/p&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;p&gt;➡️ Hundreds digit&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;170, 45, 75, 90, 802, 24, 2, 66&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The algorithm gradually builds the final ordering.&lt;/p&gt;
&lt;p&gt;That's &lt;strong&gt;Radix Sort.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;-java-7&quot;&gt;💻 Java&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class RadixSort {

    static void radixSort(int[] arr) {

        int max = arr[0];

        for (int num : arr) {
            if (num &amp;gt; max) {
                max = num;
            }
        }

        for (int exp = 1; max / exp &amp;gt; 0; exp *= 10) {

            countingSort(arr, exp);
        }
    }

    static void countingSort(int[] arr, int exp) {

        int n = arr.length;

        int[] output = new int[n];
        int[] count = new int[10];

        for (int num : arr) {

            int digit = (num / exp) % 10;

            count[digit]++;
        }

        for (int i = 1; i &amp;lt; 10; i++) {

            count[i] += count[i - 1];
        }

        for (int i = n - 1; i &amp;gt;= 0; i--) {

            int digit = (arr[i] / exp) % 10;

            output[count[digit] - 1] = arr[i];

            count[digit]--;
        }

        for (int i = 0; i &amp;lt; n; i++) {

            arr[i] = output[i];
        }
    }

    public static void main(String[] args) {

        int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};

        radixSort(arr);

        for (int num : arr) {
            System.out.print(num + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; O(d × (n + k))&lt;br&gt;
&lt;strong&gt;Space:&lt;/strong&gt; O(n + k)&lt;/p&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Radix = Digit by digit&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-9-bucket-sort-group-first-sort-later&quot;&gt;🪣 9. Bucket Sort — Group first, sort later&lt;/h2&gt;
&lt;p&gt;One last strategy.&lt;/p&gt;
&lt;p&gt;Instead of throwing every piece of clothing into one giant pile...&lt;/p&gt;
&lt;p&gt;You create buckets.&lt;/p&gt;
&lt;p&gt;🪣 Bucket 1 → small values&lt;br&gt;
🪣 Bucket 2 → medium values&lt;br&gt;
🪣 Bucket 3 → large values&lt;/p&gt;
&lt;p&gt;Then sort each bucket individually.&lt;/p&gt;
&lt;p&gt;Finally...&lt;/p&gt;
&lt;p&gt;Combine them.&lt;/p&gt;
&lt;p&gt;That's &lt;strong&gt;Bucket Sort.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;-java-8&quot;&gt;💻 Java&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;import java.util.*;

public class BucketSort {

    static void bucketSort(float[] arr) {

        int n = arr.length;

        List&amp;lt;Float&amp;gt;[] buckets = new ArrayList[n];

        for (int i = 0; i &amp;lt; n; i++) {
            buckets[i] = new ArrayList&amp;lt;&amp;gt;();
        }

        for (float num : arr) {

            int index = (int) (num * n);

            buckets[index].add(num);
        }

        for (List&amp;lt;Float&amp;gt; bucket : buckets) {
            Collections.sort(bucket);
        }

        int index = 0;

        for (List&amp;lt;Float&amp;gt; bucket : buckets) {

            for (float num : bucket) {

                arr[index++] = num;
            }
        }
    }

    public static void main(String[] args) {

        float[] arr = {
            0.42f, 0.32f, 0.23f,
            0.52f, 0.25f, 0.47f, 0.51f
        };

        bucketSort(arr);

        for (float num : arr) {
            System.out.print(num + &quot; &quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Average:&lt;/strong&gt;O(n + k)&lt;br&gt;
&lt;strong&gt;Worst:&lt;/strong&gt; O(n²)&lt;/p&gt;
&lt;p&gt;🧠 &lt;strong&gt;Remember:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Bucket = Distribute → Sort → Combine&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-so-which-algorithm-would-you-use-&quot;&gt;🧠 So... which algorithm would YOU use?&lt;/h2&gt;
&lt;p&gt;Now look back at your messy wardrobe.&lt;/p&gt;
&lt;p&gt;Same problem.&lt;/p&gt;
&lt;p&gt;Different strategies.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Algorithm    &lt;/th&gt;&lt;th&gt;Wardrobe Strategy        &lt;/th&gt;&lt;th&gt;Main Idea&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Bubble    &lt;/td&gt;&lt;td&gt;Compare neighbors        &lt;/td&gt;&lt;td&gt;Swap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;🎯 Selection &lt;/td&gt;&lt;td&gt;Find the right item      &lt;/td&gt;&lt;td&gt;Select&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;🃏 Insertion &lt;/td&gt;&lt;td&gt;Place one item at a time &lt;/td&gt;&lt;td&gt;Insert&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;✂️ Merge     &lt;/td&gt;&lt;td&gt;Split the wardrobe       &lt;/td&gt;&lt;td&gt;Divide &amp;amp; Merge&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;⚡ Quick      &lt;/td&gt;&lt;td&gt;Pick a reference item    &lt;/td&gt;&lt;td&gt;Pivot&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;🌳 Heap      &lt;/td&gt;&lt;td&gt;Prioritize items         &lt;/td&gt;&lt;td&gt;Heap&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;🔢 Counting  &lt;/td&gt;&lt;td&gt;Count categories         &lt;/td&gt;&lt;td&gt;Frequency&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;📚 Radix     &lt;/td&gt;&lt;td&gt;Sort digit by digit      &lt;/td&gt;&lt;td&gt;Digits&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;🪣 Bucket    &lt;/td&gt;&lt;td&gt;Make groups first        &lt;/td&gt;&lt;td&gt;Distribution&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And here's the part I want you to remember:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sorting isn't just about arranging numbers.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It's about learning that &lt;strong&gt;the same problem can have completely different solutions.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And in DSA, the real question isn't:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&quot;Can I sort this?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It's:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;How efficiently can I sort this?&quot;&lt;/strong&gt; 👀&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That's where &lt;strong&gt;Time Complexity&lt;/strong&gt; enters the story.&lt;/p&gt;
&lt;p&gt;Because if your wardrobe has 10 clothes, almost anything works.&lt;/p&gt;
&lt;p&gt;But what if it has &lt;strong&gt;10 lakh clothes?&lt;/strong&gt; 😭&lt;/p&gt;
&lt;p&gt;Suddenly, your choice of algorithm matters.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28251/what-if-your-wardrobe-could-teach-you-sorting-algorithms</guid>
<pubDate>Wed, 23 Sep 2026 05:05:37 +0000</pubDate>
</item>
<item>
<title>LeetCode #198. House Robber</title>
<link>https://coderlegion.com/27438/leetcode-198-house-robber</link>
<description>&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night.&lt;/p&gt;
&lt;p&gt;Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: nums = [1,2,3,1]&lt;br&gt;
Output: 4&lt;br&gt;
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).&lt;br&gt;
Total amount you can rob = 1 + 3 = 4.&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: nums = [2,7,9,3,1]&lt;br&gt;
Output: 12&lt;br&gt;
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).&lt;br&gt;
Total amount you can rob = 2 + 9 + 1 = 12.&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;1 &amp;lt;= nums.length &amp;lt;= 100&lt;br&gt;
0 &amp;lt;= nums[i] &amp;lt;= 400&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The problem can be solved using dynamic programming.We create a new array dp and use it to store the maximum up to a certain index. The first index has the value stored at the first index in nums.The second one takes the maximum of the first 2 values in nums.&lt;br&gt;
We then iterate through the rest of the array and store in dp,the maximum of the previous value in dp and the sum of the current value in nums and the value before the previous value in dp. We return the last value in dp once we are done traversing.&lt;/p&gt;
&lt;p&gt;Java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public int rob(int[] nums) {
         if(nums.length==0) return 0;
         if(nums.length==1)return nums[0];
         if(nums.length==2)return Math.max(nums[0],nums[1]);
         int [] loot = new int[nums.length];
          loot[0]= nums[0];
          loot[1]= Math.max(nums[1],nums[0]);
          for(int i=2;i&amp;lt;loot.length;i++){
            loot[i]= Math.max(nums[i]+loot[i-2],loot[i-1]);
          }
          return loot[loot.length-1];
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JavaScript&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var rob = function(nums) {
  if(nums.length==0) return 0;
  if(nums.length==1)return nums[0];
  if(nums.length==2)return Math.max(nums[0],nums[1]);
  const loot = new Array(nums.length);
  loot[0]= nums[0];
  loot[1]= Math.max(nums[1],nums[0]);
  for(let i=2;i&amp;lt;loot.length;i++){
   let previousloot= loot[i-1];
   let currentloot= nums[i]+loot[i-2];
   loot[i]= Math.max(currentloot,previousloot);
  }
  return loot[loot.length-1];
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C#&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Solution {
    public int Rob(int[] nums) {
     if(nums.Length==0) return 0;
     if(nums.Length==1)return nums[0];
     if(nums.Length==2)return Math.Max(nums[0],nums[1]);
     int [] loot = new int[nums.Length];
     loot[0]= nums[0];
     loot[1]= Math.Max(nums[1],nums[0]);
     for(int i=2;i&amp;lt;loot.Length;i++)loot[i]= Math.Max(nums[i]+loot[i-2],loot[i-1]);
     return loot[loot.Length-1];   
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C++&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int rob(vector&amp;lt;int&amp;gt;&amp;amp; nums) {
     if(nums.size()==0) return 0;
     if(nums.size()==1)return nums.at(0);
     if(nums.size()==2)return std::max(nums.at(0),nums.at(1));
     vector&amp;lt;int&amp;gt; loot;
     loot.push_back(nums.at(0));
     loot.push_back(std::max(nums.at(1),nums.at(0)));
     for(int i=2;i&amp;lt;nums.size();i++){
      loot.push_back(std::max(nums.at(i)+loot.at(i-2),loot.at(i-1)));
     }
     return loot.at(loot.size()-1);  
    }
};
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27438/leetcode-198-house-robber</guid>
<pubDate>Wed, 16 Sep 2026 20:24:40 +0000</pubDate>
</item>
<item>
<title>Sliding Window Pattern in DSA: A Beginner-Friendly Guide</title>
<link>https://coderlegion.com/27067/sliding-window-pattern-in-dsa-a-beginner-friendly-guide</link>
<description>&lt;p&gt;When I first started solving array and string problems, &lt;strong&gt;Sliding Window&lt;/strong&gt; was one of the patterns that felt confusing.&lt;/p&gt;
&lt;p&gt;The code itself is usually short, but understanding &lt;strong&gt;when to move the window, what to remove, and what to add&lt;/strong&gt; can be difficult at first.&lt;/p&gt;
&lt;p&gt;This blog explains the Sliding Window technique from the basics with a simple example.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-is-sliding-window-&quot;&gt;What is Sliding Window?&lt;/h3&gt;
&lt;p&gt;Sliding Window is a technique used to solve problems involving &lt;strong&gt;contiguous portions of an array or string&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead of repeatedly calculating the same elements, we maintain a window and move it through the input.&lt;/p&gt;
&lt;p&gt;For example, consider:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums = [1, 12, -5, -6, 50, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the window size is &lt;code&gt;4&lt;/code&gt;, our windows are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 12, -5, -6]
[12, -5, -6, 50]
[-5, -6, 50, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The window moves one position at a time.&lt;/p&gt;
&lt;p&gt;That is why it is called &lt;strong&gt;Sliding Window&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-do-we-need-sliding-window-&quot;&gt;Why Do We Need Sliding Window?&lt;/h2&gt;
&lt;p&gt;Let's say we want to find the maximum sum of any &lt;code&gt;k&lt;/code&gt; consecutive elements.&lt;/p&gt;
&lt;p&gt;For:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums = [1, 12, -5, -6, 50, 3]
k = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could calculate every window from scratch:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 + 12 + (-5) + (-6) = 2

12 + (-5) + (-6) + 50 = 51

(-5) + (-6) + 50 + 3 = 42
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works, but we are repeatedly calculating elements that were already included in the previous window.&lt;/p&gt;
&lt;p&gt;Sliding Window avoids this unnecessary work.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-main-idea&quot;&gt;The Main Idea&lt;/h2&gt;
&lt;p&gt;Look at the first window:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 12, -5, -6]  50  3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Its sum is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we slide the window one position to the right:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1  [12, -5, -6, 50]  3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What changed?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt; left the window.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;50&lt;/code&gt; entered the window.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So instead of calculating the entire sum again:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;new sum = old sum - element leaving + element entering
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;new sum = 2 - 1 + 50
        = 51
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple idea is the heart of the &lt;strong&gt;fixed-size Sliding Window&lt;/strong&gt; technique.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;fixed-size-sliding-window&quot;&gt;Fixed-Size Sliding Window&lt;/h2&gt;
&lt;p&gt;The general pattern looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;window = sum(nums[:k])

for i in range(k, len(nums)):
    window = window - nums[i-k] + nums[i]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important line is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;window = window - nums[i-k] + nums[i]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It means:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Remove the element that is leaving
+
Add the element that is entering
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;example-leetcode-643&quot;&gt;Example: LeetCode 643&lt;/h2&gt;
&lt;p&gt;One beginner-friendly problem for learning Sliding Window is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Maximum Average Subarray I&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Given an integer array &lt;code&gt;nums&lt;/code&gt; and an integer &lt;code&gt;k&lt;/code&gt;, find the contiguous subarray of length &lt;code&gt;k&lt;/code&gt; that has the maximum average.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums = [1, 12, -5, -6, 50, 3]
k = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The possible windows are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 12, -5, -6]       → sum = 2
[12, -5, -6, 50]      → sum = 51
[-5, -6, 50, 3]       → sum = 42
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The maximum sum is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;51
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore, the maximum average is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;51 / 4 = 12.75
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;python-solution&quot;&gt;Python Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Solution:
    def findMaxAverage(self, nums: List[int], k: int) -&amp;gt; float:
        tot = sum(nums[:k])

        avg = tot / k
        maximum = avg

        for i in range(k, len(nums)):
            tot = tot - nums[i-k] + nums[i]

            avg = tot / k
            maximum = max(maximum, avg)

        return maximum
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's understand it step by step.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-1-calculate-the-first-window&quot;&gt;Step 1: Calculate the First Window&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;tot = sum(nums[:k])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums = [1, 12, -5, -6, 50, 3]
k = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums[:k]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;gives:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 12, -5, -6]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;tot = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-2-calculate-the-first-average&quot;&gt;Step 2: Calculate the First Average&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;avg = tot / k
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;avg = 2 / 4
    = 0.5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We store this as our current maximum:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;maximum = avg
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-3-slide-the-window&quot;&gt;Step 3: Slide the Window&lt;/h2&gt;
&lt;p&gt;Now we start from index &lt;code&gt;k&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;for i in range(k, len(nums)):
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since &lt;code&gt;k = 4&lt;/code&gt;, the first value of &lt;code&gt;i&lt;/code&gt; is &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The new element is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums[i]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums[4] = 50
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The element leaving the window is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums[i-k]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;i = 4
k = 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we get:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;i - k = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums[i-k] = nums[0] = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;tot = tot - nums[i-k] + nums[i]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;tot = 2 - 1 + 50
    = 51
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our window has now moved from:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 12, -5, -6]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[12, -5, -6, 50]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;step-4-update-the-maximum&quot;&gt;Step 4: Update the Maximum&lt;/h2&gt;
&lt;p&gt;We calculate:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;avg = tot / k
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;avg = 51 / 4
    = 12.75
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;maximum = max(maximum, avg)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The maximum becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;12.75
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same process continues until we reach the end of the array.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-pattern-to-remember&quot;&gt;The Pattern to Remember&lt;/h2&gt;
&lt;p&gt;For a fixed-size Sliding Window, remember these three steps:&lt;/p&gt;
&lt;h4 id=&quot;1-calculate-the-first-window&quot;&gt;1. Calculate the first window&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;window = sum(nums[:k])
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-slide-the-window&quot;&gt;2. Slide the window&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;window = window - nums[i-k] + nums[i]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-update-the-answer&quot;&gt;3. Update the answer&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;answer = max(answer, window)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's the basic pattern.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;time-complexity&quot;&gt;Time Complexity&lt;/h2&gt;
&lt;p&gt;A brute-force approach may repeatedly calculate the sum of every window.&lt;/p&gt;
&lt;p&gt;Sliding Window allows us to update the sum in constant time for each movement.&lt;/p&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time Complexity: O(n)
Space Complexity: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where &lt;code&gt;n&lt;/code&gt; is the number of elements in the array.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-to-recognize-a-sliding-window-problem&quot;&gt;How to Recognize a Sliding Window Problem&lt;/h2&gt;
&lt;p&gt;When reading a LeetCode problem, look for words such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;contiguous&lt;/li&gt;
&lt;li&gt;consecutive&lt;/li&gt;
&lt;li&gt;substring&lt;/li&gt;
&lt;li&gt;subarray&lt;/li&gt;
&lt;li&gt;window&lt;/li&gt;
&lt;li&gt;exactly &lt;code&gt;k&lt;/code&gt; elements&lt;/li&gt;
&lt;li&gt;at most &lt;code&gt;k&lt;/code&gt; elements&lt;/li&gt;
&lt;li&gt;longest/shortest substring&lt;/li&gt;
&lt;li&gt;maximum/minimum sum of consecutive elements&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Find the maximum sum of &lt;code&gt;k&lt;/code&gt; consecutive elements.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This should immediately make you think:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fixed-size Sliding Window.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;common-mistake&quot;&gt;Common Mistake&lt;/h2&gt;
&lt;p&gt;One common mistake is recalculating the entire window every time.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;for i in range(n-k+1):
    total = sum(nums[i:i+k])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This repeatedly calculates values that were already calculated.&lt;/p&gt;
&lt;p&gt;Instead, calculate the first window once and then update it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;window = window - element_leaving + element_entering
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;fixed-vs-variable-sliding-window&quot;&gt;Fixed vs Variable Sliding Window&lt;/h2&gt;
&lt;p&gt;There are two major types of Sliding Window.&lt;/p&gt;
&lt;h4 id=&quot;fixed-size-window&quot;&gt;Fixed-size window&lt;/h4&gt;
&lt;p&gt;The window size stays the same.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;k = 4

[1, 2, 3, 4]
   ↓
[2, 3, 4, 5]
   ↓
[3, 4, 5, 6]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Problems such as &lt;strong&gt;Maximum Average Subarray I&lt;/strong&gt; use this pattern.&lt;/p&gt;
&lt;h4 id=&quot;variable-size-window&quot;&gt;Variable-size window&lt;/h4&gt;
&lt;p&gt;The window size can grow and shrink depending on a condition.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[ a b c d e ]
  ←──────→
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The window may expand when the condition is valid and shrink when the condition is violated.&lt;/p&gt;
&lt;p&gt;Variable-size Sliding Window is slightly more difficult, so it is better to learn fixed-size windows first.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;practice-roadmap&quot;&gt;Practice Roadmap&lt;/h2&gt;
&lt;p&gt;If you're new to Sliding Window, don't immediately jump into difficult problems.&lt;/p&gt;
&lt;p&gt;A good progression is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;LeetCode 643 — Maximum Average Subarray I&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LeetCode 1456 — Maximum Number of Vowels in a Substring of Given Length&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LeetCode 1343 — Number of Sub-arrays of Size K and Average Greater than or Equal to Threshold&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LeetCode 1876 — Substrings of Size Three with Distinct Characters&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LeetCode 209 — Minimum Size Subarray Sum&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LeetCode 3 — Longest Substring Without Repeating Characters&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LeetCode 1004 — Max Consecutive Ones III&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LeetCode 424 — Longest Repeating Character Replacement&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first four help build the fixed-size pattern.&lt;/p&gt;
&lt;p&gt;The later problems introduce &lt;strong&gt;variable-size windows&lt;/strong&gt;, which require a deeper understanding of when to expand and shrink the window.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;final-takeaway&quot;&gt;Final Takeaway&lt;/h2&gt;
&lt;p&gt;Sliding Window is not a completely different way of thinking about arrays.&lt;/p&gt;
&lt;p&gt;It is mainly about &lt;strong&gt;reusing information from the previous window instead of calculating everything again&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The most important idea is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Remove what leaves.
Add what enters.
Move the window.
Update the answer.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this becomes familiar, many array and string problems that initially look complicated become much easier to recognize.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start small, understand the pattern, and then increase the difficulty.&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27067/sliding-window-pattern-in-dsa-a-beginner-friendly-guide</guid>
<pubDate>Sun, 13 Sep 2026 17:09:56 +0000</pubDate>
</item>
<item>
<title>Learn Trapping Rain Water, Top K Frequent and Selection Sort with Step-by-Step Visualization 👀👀</title>
<link>https://coderlegion.com/26874/learn-trapping-rain-water-top-frequent-and-selection-sort-with-step-by-step-visualization</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer who has been obsessed with ramen lately. 😸🍜&lt;/p&gt;
&lt;p&gt;Have you used &lt;strong&gt;DSA View View&lt;/strong&gt; already? 👀👀&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-letearn-together-dsa-view-view--djo&quot;&gt;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-letearn-together-dsa-view-view--djo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;DSA View View allows you to understand DSA by visualizing how your implementation actually runs.&lt;/p&gt;
&lt;p&gt;In the previous articles, we looked at problems like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two Sum&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Bubble Sort&lt;/li&gt;
&lt;li&gt;Valid Parentheses&lt;/li&gt;
&lt;li&gt;Reverse Linked List&lt;/li&gt;
&lt;li&gt;Maximum Depth of Binary Tree&lt;/li&gt;
&lt;li&gt;Number of Islands&lt;/li&gt;
&lt;li&gt;Invert Binary Tree&lt;/li&gt;
&lt;li&gt;Course Schedule&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsaew-view-two-sum-binary-search-and-bubble-sort-374o&quot;&gt;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsaew-view-two-sum-binary-search-and-bubble-sort-374o&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/learn-valid-parentheses-reverse-lin-list-and-tree-max-depth-with-step-by-step-visualization-in-3o09&quot;&gt;https://dev.to/nyaomaru/learn-valid-parentheses-reverse-lin-list-and-tree-max-depth-with-step-by-step-visualization-in-3o09&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/learn-number-of-islands-invert-binatree-and-course-schedule-with-step-by-step-visualization-in-5947&quot;&gt;https://dev.to/nyaomaru/learn-number-of-islands-invert-binatree-and-course-schedule-with-step-by-step-visualization-in-5947&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This time, let's try three more problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Trapping Rain Water&lt;/li&gt;
&lt;li&gt;Top K Frequent Elements&lt;/li&gt;
&lt;li&gt;Selection Sort&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These three problems introduce some very useful ways of thinking&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Shrink a problem from both sides
Count first, then organize by frequency
Repeatedly select the next value
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once again, the implementations are not necessarily huge.&lt;/p&gt;
&lt;p&gt;But there are several changing values that we need to keep in our heads.&lt;/p&gt;
&lt;p&gt;So instead of only reading the final code,&lt;/p&gt;
&lt;p&gt;Let's view what actually happens. 👀👀&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/wuoyy4hccntcxgzey9v2.png&quot; alt=&quot;View View Case Closed&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-trapping-rain-water&quot;&gt;🌧️ Trapping Rain Water&lt;/h3&gt;
&lt;p&gt;Let's start with &lt;strong&gt;Trapping Rain Water&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have these heights&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[0, 1, 0, 2, 1, 0, 1, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we draw them as walls, it looks roughly like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;              █
      █       █
  █   █ █   █ █
-----------------
0 1 0 2 1 0 1 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rain falls from above.&lt;/p&gt;
&lt;p&gt;Some water escapes.&lt;/p&gt;
&lt;p&gt;But some water becomes trapped between taller walls.&lt;/p&gt;
&lt;p&gt;For example&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;      █~~~~~~~█
  █~~~█~~~~~~~█
-----------------
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the question is&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;How much water can be trapped?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At first, I found this problem quite confusing. 😿&lt;/p&gt;
&lt;p&gt;Because the amount of water above one position depends on walls somewhere else.&lt;/p&gt;
&lt;p&gt;So what information do we actually need?&lt;/p&gt;
&lt;h4 id=&quot;how-much-water-fits-above-one-position-&quot;&gt;How Much Water Fits Above One Position?&lt;/h4&gt;
&lt;p&gt;Imagine this position&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left wall     right wall
    █             █
    █      x      █
    █      █      █
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The water level cannot be higher than the shorter side.&lt;/p&gt;
&lt;p&gt;So the maximum possible water level is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.min(leftMax, rightMax);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we subtract the current height.&lt;/p&gt;
&lt;p&gt;Conceptually:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;water = min(leftMax, rightMax) - currentHeight
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's the basic idea.&lt;/p&gt;
&lt;p&gt;But do we really need to calculate both sides again for every position?&lt;/p&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;p&gt;We can use &lt;strong&gt;two pointers&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;two-pointers&quot;&gt;Two Pointers&lt;/h4&gt;
&lt;p&gt;Here is the implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function trap(height: number[]): number {
  let left = 0;
  let right = height.length - 1;

  let leftMax = 0;
  let rightMax = 0;

  let water = 0;

  while (left &amp;lt;= right) {
    if (height[left] &amp;lt;= height[right]) {
      if (height[left] &amp;gt;= leftMax) {
        leftMax = height[left];
      } else {
        water += leftMax - height[left];
      }

      left++;
    } else {
      if (height[right] &amp;gt;= rightMax) {
        rightMax = height[right];
      } else {
        water += rightMax - height[right];
      }

      right--;
    }
  }

  return water;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are several important values.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left
right
leftMax
rightMax
water
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is exactly the kind of code where I understand every variable individually,&lt;/p&gt;
&lt;p&gt;but then lose track of all of them together. 😹&lt;/p&gt;
&lt;p&gt;Let's follow a smaller example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[2, 0, 1, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;start-from-both-ends&quot;&gt;Start From Both Ends&lt;/h4&gt;
&lt;p&gt;At first&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = 0
right = 3

[2, 0, 1, 3]
 ↑        ↑
left    right
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The heights are:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;height[left]  = 2
height[right] = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2 &amp;lt;= 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we process the left side.&lt;/p&gt;
&lt;p&gt;There is a wall with height &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;leftMax = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then move &lt;code&gt;left&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[2, 0, 1, 3]
    ↑     ↑
   left right
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;now-we-can-trap-water&quot;&gt;Now We Can Trap Water&lt;/h4&gt;
&lt;p&gt;The current height is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But we already know there is a wall of height &lt;code&gt;2&lt;/code&gt; on the left.&lt;/p&gt;
&lt;p&gt;And the right side is currently at least as high as that.&lt;/p&gt;
&lt;p&gt;So this position can hold&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;leftMax - height[left]
= 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;water = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move again.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[2, 0, 1, 3]
       ↑  ↑
      left right
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;height[left] = 1
leftMax = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2 - 1 = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One more unit of water.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;water = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eventually we reach the final wall.&lt;/p&gt;
&lt;p&gt;Done! 🎉&lt;/p&gt;
&lt;h4 id=&quot;why-can-we-process-the-shorter-side-&quot;&gt;Why Can We Process the Shorter Side?&lt;/h4&gt;
&lt;p&gt;This part is the important idea.&lt;/p&gt;
&lt;p&gt;Suppose&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;height[left] &amp;lt;= height[right]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we already know there is a wall on the right that is at least as tall as the current left wall.&lt;/p&gt;
&lt;p&gt;So for the current left position, the limiting factor is the best wall we have seen from the left.&lt;/p&gt;
&lt;p&gt;That's why we can safely calculate&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;leftMax - height[left];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;without knowing every future wall.&lt;/p&gt;
&lt;p&gt;The same logic works from the other side.&lt;/p&gt;
&lt;p&gt;If&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;height[right] &amp;lt; height[left]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we process the right side using &lt;code&gt;rightMax&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So the algorithm keeps shrinking the unknown area&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;L → → →     ← ← ← R
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;until everything has been processed.&lt;/p&gt;
&lt;h4 id=&quot;complexity&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;Each pointer only moves across the array once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n)
Space: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This problem is a perfect example of why I like visualization.&lt;/p&gt;
&lt;p&gt;The code contains&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left
right
leftMax
rightMax
water
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And they all change at different times.&lt;/p&gt;
&lt;p&gt;When I only read&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;water += leftMax - height[left];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I might ask:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why are we using &lt;code&gt;leftMax&lt;/code&gt; here?&lt;/li&gt;
&lt;li&gt;What is &lt;code&gt;rightMax&lt;/code&gt; right now?&lt;/li&gt;
&lt;li&gt;Why did we move &lt;code&gt;left&lt;/code&gt; instead of &lt;code&gt;right&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;How much water have we already counted?&lt;/li&gt;
&lt;li&gt;Which part of the array is still unprocessed?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;😿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/99c3276sutwwa5j7npzb.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHJhcHBpbmctcbi13YXRlciIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHJhcHBpbmctcbi13YXRlciIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Step by step, we can actually watch the search area shrink.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;L         R
↓         ↓
[2, 0, 1, 3]

    L     R
    ↓     ↓
[2, 0, 1, 3]

       L  R
       ↓  ↓
[2, 0, 1, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And at the same time&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;leftMax
rightMax
water
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;keep changing.&lt;/p&gt;
&lt;p&gt;The algorithm is really asking&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Which side can I safely solve right now?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then it solves that side and moves inward. 🌧️😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-top-k-frequent-elements&quot;&gt;🔢 Top K Frequent Elements&lt;/h3&gt;
&lt;p&gt;Next, let's find the &lt;strong&gt;Top K Frequent Elements&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 1, 1, 2, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;k = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How often does each number appear?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 3 times
2 → 2 times
3 → 1 time
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the two most frequent values are&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple enough.&lt;/p&gt;
&lt;p&gt;But how should we implement it?&lt;/p&gt;
&lt;h4 id=&quot;first-count-everything&quot;&gt;First, Count Everything&lt;/h4&gt;
&lt;p&gt;The first thing we need is frequency.&lt;/p&gt;
&lt;p&gt;We can use a &lt;code&gt;Map&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const frequency = new Map&amp;lt;number, number&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then count each value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (const num of nums) {
  frequency.set(num, (frequency.get(num) ?? 0) + 1);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 1, 1, 2, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we get&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;frequency = {
  1 → 3
  2 → 2
  3 → 1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nice.&lt;/p&gt;
&lt;p&gt;But we still need the &lt;strong&gt;top K&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Of course, we could sort everything by frequency.&lt;/p&gt;
&lt;p&gt;But there is another interesting approach.&lt;/p&gt;
&lt;h4 id=&quot;use-frequency-as-an-index&quot;&gt;Use Frequency as an Index&lt;/h4&gt;
&lt;p&gt;The maximum possible frequency is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;nums.length
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we can create buckets.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const buckets: number[][] = Array.from({ length: nums.length + 1 }, () =&amp;gt; []);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The index represents frequency.&lt;/p&gt;
&lt;p&gt;For example&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;bucket[1] = values appearing 1 time
bucket[2] = values appearing 2 times
bucket[3] = values appearing 3 times
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For our example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;frequency = {
  1 → 3
  2 → 2
  3 → 1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the buckets become&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;index 0 → []
index 1 → [3]
index 2 → [2]
index 3 → [1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's pretty interesting. 👀👀&lt;/p&gt;
&lt;p&gt;Instead of asking&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;What is the frequency of this number?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;we reverse the relationship&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Which numbers have this frequency?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;implementation&quot;&gt;Implementation&lt;/h4&gt;
&lt;p&gt;Here is the full implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function topKFrequent(nums: number[], k: number): number[] {
  const frequency = new Map&amp;lt;number, number&amp;gt;();

  for (const num of nums) {
    frequency.set(num, (frequency.get(num) ?? 0) + 1);
  }

  const buckets: number[][] = Array.from({ length: nums.length + 1 }, () =&amp;gt; []);

  for (const [num, count] of frequency) {
    buckets[count].push(num);
  }

  const result: number[] = [];

  for (let count = buckets.length - 1; count &amp;gt;= 0; count--) {
    for (const num of buckets[count]) {
      result.push(num);

      if (result.length === k) {
        return result;
      }
    }
  }

  return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's follow it.&lt;/p&gt;
&lt;h4 id=&quot;step-1-build-the-frequency-map&quot;&gt;Step 1: Build the Frequency Map&lt;/h4&gt;
&lt;p&gt;Start&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;frequency = {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Read the first &lt;code&gt;1&lt;/code&gt;. And another one and another...&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 1
1 → 2
1 → 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then &lt;code&gt;2&lt;/code&gt;. And another one.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 3
2 → 1
2 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally &lt;code&gt;3&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 3
2 → 2
3 → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done.&lt;/p&gt;
&lt;h4 id=&quot;step-2-put-values-into-buckets&quot;&gt;Step 2: Put Values Into Buckets&lt;/h4&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;buckets[count].push(num);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we do&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;buckets[3].push(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we do&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;buckets[2].push(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3 → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;becomes&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;buckets[1].push(3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0: []
1: [3]
2: [2]
3: [1]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-3-read-from-highest-frequency&quot;&gt;Step 3: Read From Highest Frequency&lt;/h4&gt;
&lt;p&gt;We want the &lt;strong&gt;most frequent&lt;/strong&gt; values.&lt;/p&gt;
&lt;p&gt;So don't start at &lt;code&gt;0&lt;/code&gt;. Start from the end.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3 → [1]
2 → [2]
1 → [3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Take &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;result = [1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We still need one more. Move down.&lt;/p&gt;
&lt;p&gt;Take &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;result = [1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;result.length === k
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So return.&lt;/p&gt;
&lt;p&gt;Done! 🎉&lt;/p&gt;
&lt;h4 id=&quot;why-is-this-interesting-&quot;&gt;Why Is This Interesting?&lt;/h4&gt;
&lt;p&gt;I like this solution because the second structure changes our perspective.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Map&lt;/code&gt; says&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;value → frequency
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The buckets say&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;frequency → values
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same information. But different direction.&lt;/p&gt;
&lt;p&gt;And suddenly finding the most frequent values becomes easy.&lt;/p&gt;
&lt;p&gt;We just walk backward through the buckets.&lt;/p&gt;
&lt;h4 id=&quot;complexity-1&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;We count every number once.&lt;/p&gt;
&lt;p&gt;We distribute every unique number into a bucket.&lt;/p&gt;
&lt;p&gt;Then we walk through the buckets.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n)
Space: O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-1&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;There are two transformations happening here.&lt;/p&gt;
&lt;p&gt;First&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;nums
 ↓
frequency Map
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;frequency Map
 ↓
buckets
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;buckets
 ↓
result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reading the final implementation, it can be easy to miss why we're building two different data structures.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/qrfwdz231emdsdvlp19x.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidG9wLWstZnJlcbnQiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidG9wLWstZnJlcbnQiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With the runtime visible, we can follow the data changing shape.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 1, 1, 2, 2, 3]
        ↓ count
1 → 3
2 → 2
3 → 1
        ↓ bucket
1: [3]
2: [2]
3: [1]
        ↓ highest first
[1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's the part I like.&lt;/p&gt;
&lt;p&gt;We don't magically find the top K.&lt;/p&gt;
&lt;p&gt;We reorganize the information until the answer becomes easy to read. 🔢😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-selection-sort&quot;&gt;👉 Selection Sort&lt;/h3&gt;
&lt;p&gt;Finally, let's sort something again!&lt;/p&gt;
&lt;p&gt;We already looked at &lt;strong&gt;Bubble Sort&lt;/strong&gt; in a previous article.&lt;/p&gt;
&lt;p&gt;This time, let's try &lt;strong&gt;Selection Sort&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[5, 3, 4, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2, 3, 4, 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Selection Sort follows a very simple idea&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Find the smallest remaining value and move it to the front.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then repeat.&lt;/p&gt;
&lt;h4 id=&quot;first-pass&quot;&gt;First Pass&lt;/h4&gt;
&lt;p&gt;Start&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[5, 3, 4, 1, 2]
 ↑
 i
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assume the first value is currently the smallest.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;minIndex = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then scan everything to the right.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5 vs 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;3&lt;/code&gt; is smaller.&lt;/p&gt;
&lt;p&gt;So:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;minIndex = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3 vs 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No change.&lt;/p&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3 vs 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;1&lt;/code&gt; is smaller.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;minIndex = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 vs 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Still &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So the smallest value is at index &lt;code&gt;3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Swap&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[5, 3, 4, 1, 2]
 ↑        ↑
 i       min
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;↓&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 3, 4, 5, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the first position is finished.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1 | 3, 4, 5, 2]
 ↑
sorted
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;repeat&quot;&gt;Repeat&lt;/h4&gt;
&lt;p&gt;Next, start from index &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1 | 3, 4, 5, 2]
     ↑
     i
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Find the smallest value in&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[3, 4, 5, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Swap.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2 | 4, 5, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again.&lt;/p&gt;
&lt;p&gt;Find the smallest remaining value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eventually&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2, 3, 4, 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sorted! 🎉&lt;/p&gt;
&lt;h4 id=&quot;implementation-1&quot;&gt;Implementation&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function selectionSort(nums: number[]): number[] {
  for (let i = 0; i &amp;lt; nums.length - 1; i++) {
    let minIndex = i;

    for (let j = i + 1; j &amp;lt; nums.length; j++) {
      if (nums[j] &amp;lt; nums[minIndex]) {
        minIndex = j;
      }
    }

    if (minIndex !== i) {
      [nums[i], nums[minIndex]] = [nums[minIndex], nums[i]];
    }
  }

  return nums;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two important indexes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;i
minIndex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And also&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;j
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which searches through the unsorted area.&lt;/p&gt;
&lt;h4 id=&quot;why-is-it-called-selection-sort-&quot;&gt;Why Is It Called Selection Sort?&lt;/h4&gt;
&lt;p&gt;Because each pass &lt;strong&gt;selects&lt;/strong&gt; the smallest remaining value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Find smallest
  ↓
Select it
  ↓
Move it to the front
  ↓
Repeat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's basically the whole algorithm.&lt;/p&gt;
&lt;h4 id=&quot;complexity-2&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;For every position, we search through the remaining values.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time: O(n²)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We sort the array in place.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Space: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Selection Sort is not something I would normally choose for sorting a huge production dataset. 😹&lt;/p&gt;
&lt;p&gt;But as a learning algorithm, it is wonderfully visual.&lt;/p&gt;
&lt;h4 id=&quot;-let-s-view-view-it-2&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;The implementation contains nested loops.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (let i = 0; i &amp;lt; nums.length - 1; i++) {
  let minIndex = i;

  for (let j = i + 1; j &amp;lt; nums.length; j++) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reading it, I might lose track of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which area is already sorted?&lt;/li&gt;
&lt;li&gt;Where is &lt;code&gt;i&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Where is &lt;code&gt;j&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;minIndex&lt;/code&gt; currently point to?&lt;/li&gt;
&lt;li&gt;When exactly does the swap happen?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ovo6sej3kta3fzsvvpo7.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoic2VsZWN0aW9uLcnQiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoic2VsZWN0aW9uLcnQiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we visualize it, the basic pattern becomes obvious.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[5, 3, 4, 1, 2]
          ↑
       smallest

[1 | 3, 4, 5, 2]
              ↑
           smallest

[1, 2 | 4, 5, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The algorithm is continuously growing a finished area from left to right.&lt;/p&gt;
&lt;p&gt;That's Selection Sort.&lt;/p&gt;
&lt;p&gt;Pick the smallest remaining value.&lt;/p&gt;
&lt;p&gt;Put it next. And repeat. 🍥😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-did-we-actually-learn-&quot;&gt;🧠 What Did We Actually Learn?&lt;/h3&gt;
&lt;p&gt;Again, these three problems look completely different.&lt;/p&gt;
&lt;p&gt;But each one introduces a useful way of thinking.&lt;/p&gt;
&lt;h4 id=&quot;trapping-rain-water&quot;&gt;Trapping Rain Water&lt;/h4&gt;
&lt;p&gt;Use information from both sides to decide which part can already be solved safely.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Which side do I know enough about right now?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;top-k-frequent-elements&quot;&gt;Top K Frequent Elements&lt;/h4&gt;
&lt;p&gt;Sometimes counting the data is only the first step.&lt;/p&gt;
&lt;p&gt;Reorganize it into a structure where the answer becomes easy to retrieve.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Can I reorganize this information around what I actually need?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;selection-sort&quot;&gt;Selection Sort&lt;/h4&gt;
&lt;p&gt;Build the answer one permanent position at a time.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;What value belongs in this position next?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So this time we saw&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Two pointers
Frequency buckets
Selection
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three different mental models again.&lt;/p&gt;
&lt;p&gt;And just like the previous problems, the difficult part is often not the syntax.&lt;/p&gt;
&lt;p&gt;It's the changing state.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Which pointer moved?
What is the maximum now?
What is inside the Map?
Which bucket changed?
Where is minIndex?
Which part is already finished?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's a lot to keep in our heads.&lt;/p&gt;
&lt;p&gt;So instead, I want to &lt;strong&gt;view it&lt;/strong&gt;. 👀👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion&quot;&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we looked at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Trapping Rain Water with two pointers&lt;/li&gt;
&lt;li&gt;Top K Frequent Elements with frequency buckets&lt;/li&gt;
&lt;li&gt;Selection Sort&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more importantly, we followed how their state changes while they run.&lt;/p&gt;
&lt;p&gt;For Trapping Rain Water, we watched two pointers move inward while &lt;code&gt;leftMax&lt;/code&gt;, &lt;code&gt;rightMax&lt;/code&gt;, and &lt;code&gt;water&lt;/code&gt; changed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left →       ← right
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Top K Frequent Elements, we watched the same data change representation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;array
  ↓
frequency Map
  ↓
buckets
  ↓
result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Selection Sort, we watched the sorted area grow one position at a time.&lt;/p&gt;
&lt;p&gt;This is exactly the kind of thing I built &lt;strong&gt;DSA View View&lt;/strong&gt; for.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app&quot;&gt;https://dsa-view-view.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can write or load a TypeScript implementation, run it with your own inputs, and move backward and forward through the runtime.&lt;/p&gt;
&lt;p&gt;If you are learning DSA too, try viewing one of these problems step by step.&lt;/p&gt;
&lt;p&gt;And if there is a DSA problem you want me to cover next, please let me know in the comments!&lt;/p&gt;
&lt;p&gt;I still have many algorithms to learn myself. 😸&lt;/p&gt;
&lt;p&gt;Let's train our DSA muscles together! 💪&lt;/p&gt;
&lt;p&gt;If you like DSA View View, please give it a star ⭐&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See you in the next article!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26874/learn-trapping-rain-water-top-frequent-and-selection-sort-with-step-by-step-visualization</guid>
<pubDate>Fri, 11 Sep 2026 17:01:15 +0000</pubDate>
</item>
<item>
<title>LeetCode #273. Integer to English Words</title>
<link>https://coderlegion.com/26433/leetcode-273-integer-to-english-words</link>
<description>&lt;p&gt;&lt;em&gt;Recursion is a concept taught at University level. It is a concept many don't understand but it appears in coding challenges .For this reason, I have decided to cover it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;br&gt;
Convert a non-negative integer num to its English words representation.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: num = 123&lt;br&gt;
Output: &quot;One Hundred Twenty Three&quot;&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: num = 12345&lt;br&gt;
Output: &quot;Twelve Thousand Three Hundred Forty Five&quot;&lt;br&gt;
Example 3:&lt;/p&gt;
&lt;p&gt;Input: num = 1234567&lt;br&gt;
Output: &quot;One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven&quot;&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;0 &amp;lt;= num &amp;lt;= 231 - 1&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;br&gt;
For this, we can use recursion. We take base cases for some numbers and return specific words. If the number is not any of these numbers, we call the function as much as possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solutions&lt;/strong&gt;&lt;br&gt;
Java&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Solution {
    public String numberToWords(int num){
      if(num&amp;lt;10){
       if(num==0)return &quot;Zero&quot;; 
       else if(num==1)return &quot;One&quot;;
       else if(num==2) return &quot;Two&quot;;
       else if(num==3) return &quot;Three&quot;;
       else if(num==4) return &quot;Four&quot;;
       else if(num==5) return &quot;Five&quot;;
       else if(num==6) return &quot;Six&quot;;
       else if(num==7) return &quot;Seven&quot;;
       else if(num==8) return &quot;Eight&quot;;
       else return &quot;Nine&quot;;
      }else if(num&amp;gt;=10 &amp;amp;&amp;amp; num&amp;lt;100){
        if(num==10)return&quot;Ten&quot;;
        else if(num==11)return &quot;Eleven&quot;;
        else if(num==12) return &quot;Twelve&quot;;
        else if(num==13) return &quot;Thirteen&quot;;
        else if(num==14) return &quot;Fourteen&quot;;
        else if(num==15) return &quot;Fifteen&quot;;
        else if(num==16) return &quot;Sixteen&quot;;
        else if(num==17) return &quot;Seventeen&quot;;
        else if(num==18) return &quot;Eighteen&quot;;
        else if(num==19) return &quot;Nineteen&quot;;
        else if(num==20) return &quot;Twenty&quot;;
        else if(num==30) return &quot;Thirty&quot;;
        else if(num==40) return &quot;Forty&quot;;
        else if(num==50) return &quot;Fifty&quot;;
        else if(num==60) return &quot;Sixty&quot;;
        else if(num==70) return &quot;Seventy&quot;;
        else if(num==80) return &quot;Eighty&quot;;
        else if(num==90) return &quot;Ninety&quot;;
        else if(num&amp;gt;60 &amp;amp;&amp;amp; num&amp;lt;70) return &quot;Sixty &quot;+numberToWords(num-60);
        else if(num&amp;gt;70 &amp;amp;&amp;amp; num&amp;lt;80) return &quot;Seventy &quot; +numberToWords(num-70);
        else if(num&amp;gt;80 &amp;amp;&amp;amp; num&amp;lt;90) return &quot;Eighty &quot; +numberToWords(num-80);
        else if(num&amp;gt;20 &amp;amp;&amp;amp; num&amp;lt;30) return &quot;Twenty &quot;+numberToWords(num-20);
        else if(num&amp;gt;30 &amp;amp;&amp;amp; num&amp;lt;40) return &quot;Thirty &quot; +numberToWords(num-30);
        else if(num&amp;gt;40 &amp;amp;&amp;amp; num&amp;lt;50) return &quot;Forty &quot; +numberToWords(num-40);
        else if(num&amp;gt;50 &amp;amp;&amp;amp; num&amp;lt;60) return &quot;Fifty &quot;+numberToWords(num-50);
        else if(num&amp;gt;60 &amp;amp;&amp;amp; num&amp;lt;70) return &quot;Sixty &quot;+numberToWords(num-60);
        else if(num&amp;gt;70 &amp;amp;&amp;amp; num&amp;lt;80) return &quot;Seventy &quot; +numberToWords(num-70);
        else if(num&amp;gt;80 &amp;amp;&amp;amp; num&amp;lt;90) return &quot;Eighty &quot; +numberToWords(num-80);
        else  return &quot;Ninety &quot;+numberToWords(num-90);
      }else if(num&amp;gt;=100 &amp;amp;&amp;amp; num&amp;lt;1000){
        int numHundred=num/100;
        int modHundred=num%100;
        if(modHundred==0) return numberToWords(numHundred)+&quot; Hundred&quot;;
        else return numberToWords(numHundred)+&quot; Hundred &quot;+numberToWords(modHundred);
      }else if(num&amp;gt;=1000 &amp;amp;&amp;amp; num&amp;lt;1000000){
        int numThousand=num/1000;
        int modThousand=num%1000;
        if(modThousand==0) return numberToWords(numThousand)+&quot; Thousand&quot;;
        else return numberToWords(numThousand)+&quot; Thousand &quot;+numberToWords(modThousand);
      }else if(num&amp;gt;=1000000 &amp;amp;&amp;amp; num&amp;lt;1000000000){
       int numMillion=num/1000000;
       int modMillion=num%1000000;
        if(modMillion==0) return numberToWords(numMillion)+&quot; Million&quot;;
        else return numberToWords(numMillion)+&quot; Million &quot;+numberToWords(modMillion);
      }else{
       int numBillion=num/1000000000; 
       int modBillion=num%1000000000;
        if(modBillion==0) return numberToWords(numBillion)+&quot; Billion&quot;;
        else return numberToWords(numBillion)+&quot; Billion &quot;+numberToWords(modBillion);
      }    
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C#&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;public class Solution {
    public string NumberToWords(int num) {
      if(num&amp;lt;10){
       if(num==0)return &quot;Zero&quot;; 
       else if(num==1)return &quot;One&quot;;
       else if(num==2) return &quot;Two&quot;;
       else if(num==3) return &quot;Three&quot;;
       else if(num==4) return &quot;Four&quot;;
       else if(num==5) return &quot;Five&quot;;
       else if(num==6) return &quot;Six&quot;;
       else if(num==7) return &quot;Seven&quot;;
       else if(num==8) return &quot;Eight&quot;;
       else return &quot;Nine&quot;;
      }else if(num&amp;gt;=10 &amp;amp;&amp;amp; num&amp;lt;100){
        if(num==10)return&quot;Ten&quot;;
        else if(num==11)return &quot;Eleven&quot;;
        else if(num==12) return &quot;Twelve&quot;;
        else if(num==13) return &quot;Thirteen&quot;;
        else if(num==14) return &quot;Fourteen&quot;;
        else if(num==15) return &quot;Fifteen&quot;;
        else if(num==16) return &quot;Sixteen&quot;;
        else if(num==17) return &quot;Seventeen&quot;;
        else if(num==18) return &quot;Eighteen&quot;;
        else if(num==19) return &quot;Nineteen&quot;;
        else if(num==20) return &quot;Twenty&quot;;
        else if(num==30) return &quot;Thirty&quot;;
        else if(num==40) return &quot;Forty&quot;;
        else if(num==50) return &quot;Fifty&quot;;
        else if(num==60) return &quot;Sixty&quot;;
        else if(num==70) return &quot;Seventy&quot;;
        else if(num==80) return &quot;Eighty&quot;;
        else if(num==90) return &quot;Ninety&quot;;
        else if(num&amp;gt;60 &amp;amp;&amp;amp; num&amp;lt;70) return &quot;Sixty &quot;+NumberToWords(num-60);
        else if(num&amp;gt;70 &amp;amp;&amp;amp; num&amp;lt;80) return &quot;Seventy &quot; +NumberToWords(num-70);
        else if(num&amp;gt;80 &amp;amp;&amp;amp; num&amp;lt;90) return &quot;Eighty &quot; +NumberToWords(num-80);
        else if(num&amp;gt;20 &amp;amp;&amp;amp; num&amp;lt;30) return &quot;Twenty &quot;+NumberToWords(num-20);
        else if(num&amp;gt;30 &amp;amp;&amp;amp; num&amp;lt;40) return &quot;Thirty &quot; +NumberToWords(num-30);
        else if(num&amp;gt;40 &amp;amp;&amp;amp; num&amp;lt;50) return &quot;Forty &quot; +NumberToWords(num-40);
        else if(num&amp;gt;50 &amp;amp;&amp;amp; num&amp;lt;60) return &quot;Fifty &quot;+NumberToWords(num-50);
        else if(num&amp;gt;60 &amp;amp;&amp;amp; num&amp;lt;70) return &quot;Sixty &quot;+NumberToWords(num-60);
        else if(num&amp;gt;70 &amp;amp;&amp;amp; num&amp;lt;80) return &quot;Seventy &quot; +NumberToWords(num-70);
        else if(num&amp;gt;80 &amp;amp;&amp;amp; num&amp;lt;90) return &quot;Eighty &quot; +NumberToWords(num-80);
        else  return &quot;Ninety &quot;+NumberToWords(num-90);
      }else if(num&amp;gt;=100 &amp;amp;&amp;amp; num&amp;lt;1000){
        int numHundred=num/100;
        int modHundred=num%100;
        if(modHundred==0) return NumberToWords(numHundred)+&quot; Hundred&quot;;
        else return NumberToWords(numHundred)+&quot; Hundred &quot;+NumberToWords(modHundred);
      }else if(num&amp;gt;=1000 &amp;amp;&amp;amp; num&amp;lt;1000000){
        int numThousand=num/1000;
        int modThousand=num%1000;
        if(modThousand==0) return NumberToWords(numThousand)+&quot; Thousand&quot;;
        else return NumberToWords(numThousand)+&quot; Thousand &quot;+NumberToWords(modThousand);
      }else if(num&amp;gt;=1000000 &amp;amp;&amp;amp; num&amp;lt;1000000000){
       int numMillion=num/1000000;
       int modMillion=num%1000000;
        if(modMillion==0) return NumberToWords(numMillion)+&quot; Million&quot;;
        else return NumberToWords(numMillion)+&quot; Million &quot;+NumberToWords(modMillion);
      }else{
       int numBillion=num/1000000000; 
       int modBillion=num%1000000000;
        if(modBillion==0) return NumberToWords(numBillion)+&quot; Billion&quot;;
        else return NumberToWords(numBillion)+&quot; Billion &quot;+NumberToWords(modBillion);
      }       
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JavaScript&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var numberToWords = function(num) {
  if(num&amp;lt;10){
       if(num==0)return &quot;Zero&quot;; 
       else if(num==1)return &quot;One&quot;;
       else if(num==2) return &quot;Two&quot;;
       else if(num==3) return &quot;Three&quot;;
       else if(num==4) return &quot;Four&quot;;
       else if(num==5) return &quot;Five&quot;;
       else if(num==6) return &quot;Six&quot;;
       else if(num==7) return &quot;Seven&quot;;
       else if(num==8) return &quot;Eight&quot;;
       else return &quot;Nine&quot;;
      }else if(num&amp;gt;=10 &amp;amp;&amp;amp; num&amp;lt;100){
        if(num==10)return&quot;Ten&quot;;
        else if(num==11)return &quot;Eleven&quot;;
        else if(num==12) return &quot;Twelve&quot;;
        else if(num==13) return &quot;Thirteen&quot;;
        else if(num==14) return &quot;Fourteen&quot;;
        else if(num==15) return &quot;Fifteen&quot;;
        else if(num==16) return &quot;Sixteen&quot;;
        else if(num==17) return &quot;Seventeen&quot;;
        else if(num==18) return &quot;Eighteen&quot;;
        else if(num==19) return &quot;Nineteen&quot;;
        else if(num==20) return &quot;Twenty&quot;;
        else if(num==30) return &quot;Thirty&quot;;
        else if(num==40) return &quot;Forty&quot;;
        else if(num==50) return &quot;Fifty&quot;;
        else if(num==60) return &quot;Sixty&quot;;
        else if(num==70) return &quot;Seventy&quot;;
        else if(num==80) return &quot;Eighty&quot;;
        else if(num==90) return &quot;Ninety&quot;;
        else if(num&amp;gt;60 &amp;amp;&amp;amp; num&amp;lt;70) return &quot;Sixty &quot;+numberToWords(num-60);
        else if(num&amp;gt;70 &amp;amp;&amp;amp; num&amp;lt;80) return &quot;Seventy &quot; +numberToWords(num-70);
        else if(num&amp;gt;80 &amp;amp;&amp;amp; num&amp;lt;90) return &quot;Eighty &quot; +numberToWords(num-80);
        else if(num&amp;gt;20 &amp;amp;&amp;amp; num&amp;lt;30) return &quot;Twenty &quot;+numberToWords(num-20);
        else if(num&amp;gt;30 &amp;amp;&amp;amp; num&amp;lt;40) return &quot;Thirty &quot; +numberToWords(num-30);
        else if(num&amp;gt;40 &amp;amp;&amp;amp; num&amp;lt;50) return &quot;Forty &quot; +numberToWords(num-40);
        else if(num&amp;gt;50 &amp;amp;&amp;amp; num&amp;lt;60) return &quot;Fifty &quot;+numberToWords(num-50);
        else if(num&amp;gt;60 &amp;amp;&amp;amp; num&amp;lt;70) return &quot;Sixty &quot;+numberToWords(num-60);
        else if(num&amp;gt;70 &amp;amp;&amp;amp; num&amp;lt;80) return &quot;Seventy &quot; +numberToWords(num-70);
        else if(num&amp;gt;80 &amp;amp;&amp;amp; num&amp;lt;90) return &quot;Eighty &quot; +numberToWords(num-80);
        else  return &quot;Ninety &quot;+numberToWords(num-90);
      }else if(num&amp;gt;=100 &amp;amp;&amp;amp; num&amp;lt;1000){
        let modHundred=num%100;
        let numHundred=(num-modHundred)/100;
        if(modHundred==0) return numberToWords(numHundred)+&quot; Hundred&quot;;
        else return numberToWords(numHundred)+&quot; Hundred &quot;+numberToWords(modHundred);
      }else if(num&amp;gt;=1000 &amp;amp;&amp;amp; num&amp;lt;1000000){
        let modThousand=num%1000;
        let numThousand=(num-modThousand)/1000;
        if(modThousand==0) return numberToWords(numThousand)+&quot; Thousand&quot;;
        else return numberToWords(numThousand)+&quot; Thousand &quot;+numberToWords(modThousand);
      }else if(num&amp;gt;=1000000 &amp;amp;&amp;amp; num&amp;lt;1000000000){
       let modMillion=num%1000000;
       let numMillion=(num-modMillion)/1000000;
        if(modMillion==0) return numberToWords(numMillion)+&quot; Million&quot;;
        else return numberToWords(numMillion)+&quot; Million &quot;+numberToWords(modMillion);
      }else{
       let modBillion=num%1000000000;
       let numBillion=(num-modBillion)/1000000000; 
        if(modBillion==0) return numberToWords(numBillion)+&quot; Billion&quot;;
        else return numberToWords(numBillion)+&quot; Billion &quot;+numberToWords(modBillion);
      }       
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C++&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;class Solution {
public:
    string numberToWords(int num) {
     if(num&amp;lt;10){
       if(num==0)return &quot;Zero&quot;; 
       else if(num==1)return &quot;One&quot;;
       else if(num==2) return &quot;Two&quot;;
       else if(num==3) return &quot;Three&quot;;
       else if(num==4) return &quot;Four&quot;;
       else if(num==5) return &quot;Five&quot;;
       else if(num==6) return &quot;Six&quot;;
       else if(num==7) return &quot;Seven&quot;;
       else if(num==8) return &quot;Eight&quot;;
       else return &quot;Nine&quot;;
      }else if(num&amp;gt;=10 &amp;amp;&amp;amp; num&amp;lt;100){
        if(num==10)return&quot;Ten&quot;;
        else if(num==11)return &quot;Eleven&quot;;
        else if(num==12) return &quot;Twelve&quot;;
        else if(num==13) return &quot;Thirteen&quot;;
        else if(num==14) return &quot;Fourteen&quot;;
        else if(num==15) return &quot;Fifteen&quot;;
        else if(num==16) return &quot;Sixteen&quot;;
        else if(num==17) return &quot;Seventeen&quot;;
        else if(num==18) return &quot;Eighteen&quot;;
        else if(num==19) return &quot;Nineteen&quot;;
        else if(num==20) return &quot;Twenty&quot;;
        else if(num==30) return &quot;Thirty&quot;;
        else if(num==40) return &quot;Forty&quot;;
        else if(num==50) return &quot;Fifty&quot;;
        else if(num==60) return &quot;Sixty&quot;;
        else if(num==70) return &quot;Seventy&quot;;
        else if(num==80) return &quot;Eighty&quot;;
        else if(num==90) return &quot;Ninety&quot;;
        else if(num&amp;gt;60 &amp;amp;&amp;amp; num&amp;lt;70) return &quot;Sixty &quot;+numberToWords(num-60);
        else if(num&amp;gt;70 &amp;amp;&amp;amp; num&amp;lt;80) return &quot;Seventy &quot; +numberToWords(num-70);
        else if(num&amp;gt;80 &amp;amp;&amp;amp; num&amp;lt;90) return &quot;Eighty &quot; +numberToWords(num-80);
        else if(num&amp;gt;20 &amp;amp;&amp;amp; num&amp;lt;30) return &quot;Twenty &quot;+numberToWords(num-20);
        else if(num&amp;gt;30 &amp;amp;&amp;amp; num&amp;lt;40) return &quot;Thirty &quot; +numberToWords(num-30);
        else if(num&amp;gt;40 &amp;amp;&amp;amp; num&amp;lt;50) return &quot;Forty &quot; +numberToWords(num-40);
        else if(num&amp;gt;50 &amp;amp;&amp;amp; num&amp;lt;60) return &quot;Fifty &quot;+numberToWords(num-50);
        else if(num&amp;gt;60 &amp;amp;&amp;amp; num&amp;lt;70) return &quot;Sixty &quot;+numberToWords(num-60);
        else if(num&amp;gt;70 &amp;amp;&amp;amp; num&amp;lt;80) return &quot;Seventy &quot; +numberToWords(num-70);
        else if(num&amp;gt;80 &amp;amp;&amp;amp; num&amp;lt;90) return &quot;Eighty &quot; +numberToWords(num-80);
        else  return &quot;Ninety &quot;+numberToWords(num-90);
      }else if(num&amp;gt;=100 &amp;amp;&amp;amp; num&amp;lt;1000){
        int numHundred=num/100;
        int modHundred=num%100;
        if(modHundred==0) return numberToWords(numHundred)+&quot; Hundred&quot;;
        else return numberToWords(numHundred)+&quot; Hundred &quot;+numberToWords(modHundred);
      }else if(num&amp;gt;=1000 &amp;amp;&amp;amp; num&amp;lt;1000000){
        int numThousand=num/1000;
        int modThousand=num%1000;
        if(modThousand==0) return numberToWords(numThousand)+&quot; Thousand&quot;;
        else return numberToWords(numThousand)+&quot; Thousand &quot;+numberToWords(modThousand);
      }else if(num&amp;gt;=1000000 &amp;amp;&amp;amp; num&amp;lt;1000000000){
       int numMillion=num/1000000;
       int modMillion=num%1000000;
        if(modMillion==0) return numberToWords(numMillion)+&quot; Million&quot;;
        else return numberToWords(numMillion)+&quot; Million &quot;+numberToWords(modMillion);
      }else{
       int numBillion=num/1000000000; 
       int modBillion=num%1000000000;
        if(modBillion==0) return numberToWords(numBillion)+&quot; Billion&quot;;
        else return numberToWords(numBillion)+&quot; Billion &quot;+numberToWords(modBillion);
      }       
    }
};
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26433/leetcode-273-integer-to-english-words</guid>
<pubDate>Mon, 07 Sep 2026 21:23:52 +0000</pubDate>
</item>
<item>
<title>Data Structure &amp; Algorithms</title>
<link>https://coderlegion.com/26242/data-structure-algorithms</link>
<description>&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/asimayazmrd0987-glitch/Data-Structures-and-Algorithms&quot;&gt;https://github.com/asimayazmrd0987-glitch/Data-Structures-and-Algorithms&lt;/a&gt;&lt;br&gt;
This is my github repo where I work on DSA, and I am Still working on it.&lt;br&gt;
It is Open Source, get it free.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26242/data-structure-algorithms</guid>
<pubDate>Sat, 05 Sep 2026 07:27:46 +0000</pubDate>
</item>
<item>
<title>Learn Number of Islands, Invert Binary Tree, and Course Schedule with Step-by-Step</title>
<link>https://coderlegion.com/26218/learn-number-of-islands-invert-binary-tree-and-course-schedule-with-step-by-step</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer who is surprised by how cold it is in the Netherlands even though it’s still summer. 😸&lt;/p&gt;
&lt;p&gt;Have you used &lt;strong&gt;DSA View View&lt;/strong&gt; already? 👀👀&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&quot;&gt;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;DSA View View allows you to understand DSA by visualizing how your implementation actually runs.&lt;/p&gt;
&lt;p&gt;In the previous articles, we looked at problems like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two Sum&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Bubble Sort&lt;/li&gt;
&lt;li&gt;Valid Parentheses&lt;/li&gt;
&lt;li&gt;Reverse Linked List&lt;/li&gt;
&lt;li&gt;Maximum Depth of Binary Tree&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort-374o&quot;&gt;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort-374o&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/learn-valid-parentheses-reverse-linked-list-and-tree-max-depth-with-step-by-step-visualization-in-3o09&quot;&gt;https://dev.to/nyaomaru/learn-valid-parentheses-reverse-linked-list-and-tree-max-depth-with-step-by-step-visualization-in-3o09&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This time, let's try three more classic problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Number of Islands&lt;/li&gt;
&lt;li&gt;Invert Binary Tree&lt;/li&gt;
&lt;li&gt;Course Schedule&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These three problems introduce some very useful ways of thinking:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Explore connected things
Transform a tree with recursion
Resolve dependencies in the right order
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementations are not huge.&lt;/p&gt;
&lt;p&gt;But the runtime can become surprisingly difficult to hold in our heads.&lt;/p&gt;
&lt;p&gt;So let's view what actually happens. 👀&lt;/p&gt;
&lt;p&gt;Let's learn together! 😸&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/618ol6g3ivydm71t0r5o.png&quot; alt=&quot;Step view 3&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-number-of-islands&quot;&gt;🏝️ Number of Islands&lt;/h3&gt;
&lt;p&gt;Let's start with &lt;strong&gt;Number of Islands&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have this grid:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1 0 0
1 0 0 1
0 0 1 1
0 0 0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;1&lt;/code&gt; means land. 🏝️&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0&lt;/code&gt; means water. 🌊&lt;/p&gt;
&lt;p&gt;Land connected vertically or horizontally belongs to the same island.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;So how many islands are there?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's look at the first group.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1
1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These cells are connected.&lt;/p&gt;
&lt;p&gt;So they form &lt;strong&gt;one island&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On the right side&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
  1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Those cells are also connected.&lt;/p&gt;
&lt;p&gt;So the answer is &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Nice! 🏝️🏝️&lt;/p&gt;
&lt;p&gt;But how do we make the code understand that several &lt;code&gt;1&lt;/code&gt;s belong to the same island?&lt;/p&gt;
&lt;h4 id=&quot;find-one-land-then-explore-all-connected-land&quot;&gt;Find One Land, Then Explore All Connected Land&lt;/h4&gt;
&lt;p&gt;The basic idea is&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;When we find a new &lt;code&gt;1&lt;/code&gt;, count one island and visit all land connected to it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's use this implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function numIslands(grid: string[][]): number {
  let islands = 0;

  const visit = (row: number, col: number): void =&amp;gt; {
    if (row &amp;lt; 0 || col &amp;lt; 0) return;
    if (row &amp;gt;= grid.length || col &amp;gt;= grid[row].length) return;
    if (grid[row][col] !== &quot;1&quot;) return;

    grid[row][col] = &quot;0&quot;;
    visit(row + 1, col);
    visit(row - 1, col);
    visit(row, col + 1);
    visit(row, col - 1);
  };

  for (let row = 0; row &amp;lt; grid.length; row++) {
    for (let col = 0; col &amp;lt; grid[row].length; col++) {
      if (grid[row][col] === &quot;1&quot;) {
        islands++;
        visit(row, col);
      }
    }
  }

  return islands;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two important parts.&lt;/p&gt;
&lt;p&gt;First, we scan the grid&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (let row = 0; row &amp;lt; grid.length; row++) {
  for (let col = 0; col &amp;lt; grid[row].length; col++) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, when we find land&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (grid[row][col] === &quot;1&quot;) {
  islands++;
  visit(row, col);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We count a new island.&lt;/p&gt;
&lt;p&gt;But then &lt;code&gt;visit()&lt;/code&gt; does something important.&lt;/p&gt;
&lt;p&gt;It removes all land connected to that island from future consideration.&lt;/p&gt;
&lt;h4 id=&quot;why-do-we-change-1-to-0-&quot;&gt;Why Do We Change &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;Inside &lt;code&gt;visit()&lt;/code&gt; we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;grid[row][col] = &quot;0&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first, changing land into water looks a little strange. 😿&lt;/p&gt;
&lt;p&gt;But here, &lt;code&gt;0&lt;/code&gt; really means&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;We already visited this land.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's follow a tiny example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1
1 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start at the top-left. And we found land! So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;islands = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;visit(0, 0);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside &lt;code&gt;visit()&lt;/code&gt;, we mark it as visited.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 1
1 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we visit the four directions&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;down
up
right
left
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Going down finds another &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 1
1 0
↑
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we visit it too.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 1
0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Going right from the original cell also finds land.&lt;/p&gt;
&lt;p&gt;Visit it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 0
0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the whole connected island has disappeared from our search.&lt;/p&gt;
&lt;p&gt;When the outer loops continue, there is no &lt;code&gt;1&lt;/code&gt; left in that island to count again.&lt;/p&gt;
&lt;p&gt;That's the key idea.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Count once, then mark the whole connected area as visited.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;why-four-recursive-calls-&quot;&gt;Why Four Recursive Calls?&lt;/h4&gt;
&lt;p&gt;We use&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;visit(row + 1, col);
visit(row - 1, col);
visit(row, col + 1);
visit(row, col - 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;        up
         ↑
left ← current → right
         ↓
        down
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each visited cell asks&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Is there more land next to me?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And every newly discovered land cell asks the same question again.&lt;/p&gt;
&lt;p&gt;This continues until we reach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;water&lt;/li&gt;
&lt;li&gt;outside the grid&lt;/li&gt;
&lt;li&gt;land we already visited&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those cases stop the recursion.&lt;/p&gt;
&lt;h4 id=&quot;the-base-cases&quot;&gt;The Base Cases&lt;/h4&gt;
&lt;p&gt;These lines protect us&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (row &amp;lt; 0 || col &amp;lt; 0) return;
if (row &amp;gt;= grid.length || col &amp;gt;= grid[row].length) return;
if (grid[row][col] !== &quot;1&quot;) return;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If we walk outside the grid, stop.&lt;/li&gt;
&lt;li&gt;If we reach water, stop.&lt;/li&gt;
&lt;li&gt;If we reach a cell we already changed to &lt;code&gt;0&lt;/code&gt;, stop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Otherwise, continue exploring.&lt;/p&gt;
&lt;h4 id=&quot;let-s-follow-two-islands&quot;&gt;Let's Follow Two Islands&lt;/h4&gt;
&lt;p&gt;Consider&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1 0
0 0 1
0 1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scan begins at the top-left.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1 0
↑
0 0 1
0 1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Found land.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;islands = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;visit()&lt;/code&gt; removes everything connected to it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 0 0
0 0 1
0 1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The loops continue.&lt;/p&gt;
&lt;p&gt;Eventually we reach&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 0 0
0 0 1
    ↑
0 1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;islands = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;visit()&lt;/code&gt; explores that entire connected area.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 0 0
0 0 0
0 0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2 islands
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🎉&lt;/p&gt;
&lt;h4 id=&quot;complexity-3&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;Every cell is processed at most a small number of times.&lt;/p&gt;
&lt;p&gt;If the grid has &lt;code&gt;m&lt;/code&gt; rows and &lt;code&gt;n&lt;/code&gt; columns&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(m × n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the worst case, the recursive call stack may grow with the number of land cells.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Space: O(m × n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-3&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is one of those algorithms where the final code is pretty small.&lt;/p&gt;
&lt;p&gt;But while reading it, there are a lot of things moving at once&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;row
col
grid
islands
recursive calls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then suddenly we see&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;grid[row][col] = &quot;0&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Why did that cell disappear?&lt;/li&gt;
&lt;li&gt;Which recursive call are we inside?&lt;/li&gt;
&lt;li&gt;Which cells belong to the current island?&lt;/li&gt;
&lt;li&gt;Where will the outer loop continue after recursion finishes?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is a lot to simulate mentally. 😿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/c2q4mnfvnw0quklb52r9.gif&quot; alt=&quot;Number Of Islands&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoibnVtYmVyLW9mLWlzbGFuZHMiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoibnVtYmVyLW9mLWlzbGFuZHMiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we view it step by step, the idea becomes much more visual.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Find land
  ↓
islands++
  ↓
visit connected land
  ↓
mark it visited
  ↓
expand up / down / left / right
  ↓
return to scanning
  ↓
find next island
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of thinking about recursion first, I like to think about it like this&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Find one island, paint the whole island away, then keep searching.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;🏝️😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-invert-binary-tree&quot;&gt;🌳 Invert Binary Tree&lt;/h3&gt;
&lt;p&gt;Next, let's invert a binary tree.&lt;/p&gt;
&lt;p&gt;Suppose we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;        4
       / \
      2   7
     / \ / \
    1  3 6  9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want to change&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;        4
       / \
      7   2
     / \ / \
    9  6 3  1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every left child becomes the right child.&lt;/p&gt;
&lt;p&gt;Every right child becomes the left child.&lt;/p&gt;
&lt;p&gt;Simple, right?&lt;/p&gt;
&lt;p&gt;Well, the final implementation is also surprisingly small.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function invertTree(root: TreeNode | null): TreeNode | null {
  if (root === null) return null;

  const left = invertTree(root.left);
  const right = invertTree(root.right);

  root.left = right;
  root.right = left;

  return root;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's almost suspiciously short. 👀&lt;/p&gt;
&lt;h4 id=&quot;go-down-first&quot;&gt;Go Down First&lt;/h4&gt;
&lt;p&gt;Let's use a smaller tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
   / \
  2   3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start at node &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But we don't swap immediately.&lt;/p&gt;
&lt;p&gt;First,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const left = invertTree(root.left);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we go to node &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Node &lt;code&gt;2&lt;/code&gt; also tries to invert its left child.&lt;/p&gt;
&lt;p&gt;But there is no child.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (root === null) return null;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;returns &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The same thing happens on the right side of node &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now node &lt;code&gt;2&lt;/code&gt; has&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = null
right = null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;root.left = right;
root.right = left;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;does not visibly change anything.&lt;/p&gt;
&lt;p&gt;Node &lt;code&gt;2&lt;/code&gt; returns.&lt;/p&gt;
&lt;p&gt;Then node &lt;code&gt;1&lt;/code&gt; explores its right subtree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node &lt;code&gt;3&lt;/code&gt; also has no children, so it returns after the same process.&lt;/p&gt;
&lt;p&gt;Only then do we come back to node &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = 2
right = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we do&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;root.left = right;
root.right = left;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
   / \
  2   3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;becomes 👇&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
   / \
  3   2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done! 🎉&lt;/p&gt;
&lt;h4 id=&quot;the-important-part-the-swap-happens-on-the-way-back&quot;&gt;The Important Part: The Swap Happens on the Way Back&lt;/h4&gt;
&lt;p&gt;This is what makes the recursive solution interesting.&lt;/p&gt;
&lt;p&gt;The function first goes down.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
↓
2
↓
null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then it comes back.&lt;/p&gt;
&lt;p&gt;Later it explores the other side.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
↓
3
↓
null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And after both children have returned, the current node swaps them.&lt;/p&gt;
&lt;p&gt;So the flow is more like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Go left
  ↓
Invert left subtree
  ↓
Go right
  ↓
Invert right subtree
  ↓
Swap the returned subtrees
  ↓
Return current node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tree transformation is built while recursion unwinds.&lt;/p&gt;
&lt;h4 id=&quot;a-slightly-bigger-example&quot;&gt;A Slightly Bigger Example&lt;/h4&gt;
&lt;p&gt;Let's look at&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;      4
     / \
    2   7
   / \
  1   3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start at &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;invertTree(4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;invertTree(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;invertTree(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node &lt;code&gt;1&lt;/code&gt; returns.&lt;/p&gt;
&lt;p&gt;Then node &lt;code&gt;3&lt;/code&gt; returns.&lt;/p&gt;
&lt;p&gt;Now node &lt;code&gt;2&lt;/code&gt; has&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = 1
right = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swap them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    2
   / \
  3   1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then recursion returns to &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The right subtree rooted at &lt;code&gt;7&lt;/code&gt; is also processed.&lt;/p&gt;
&lt;p&gt;Finally node &lt;code&gt;4&lt;/code&gt; receives&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = inverted subtree rooted at 2
right = inverted subtree rooted at 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and swaps them.&lt;/p&gt;
&lt;p&gt;The final tree becomes&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;      4
     / \
    7   2
       / \
      3   1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The interesting thing is that each node only needs to know about its own two children.&lt;/p&gt;
&lt;p&gt;It doesn't need to understand the whole tree.&lt;/p&gt;
&lt;h4 id=&quot;complexity-4&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;We visit every node once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time: O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The recursive call stack depends on the height of the tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Space: O(h)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a balanced tree&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;O(log n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the worst case&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-4&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is exactly where recursion can become difficult to mentally simulate.&lt;/p&gt;
&lt;p&gt;The code says&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const left = invertTree(root.left);
const right = invertTree(root.right);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;root.left = right;
root.right = left;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But my brain immediately starts asking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which root are we talking about now?&lt;/li&gt;
&lt;li&gt;Did node 2 already swap?&lt;/li&gt;
&lt;li&gt;Are we still going down?&lt;/li&gt;
&lt;li&gt;Or are we coming back up?&lt;/li&gt;
&lt;li&gt;What does left contain at this moment?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;😿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/bvxlf930nyny6tzvpcbd.gif&quot; alt=&quot;Invert Tree&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiaW52ZXJ0LWJpbmFyeS10cmVlIiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiaW52ZXJ0LWJpbmFyeS10cmVlIiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we step through the runtime, we can separate two different movements.&lt;/p&gt;
&lt;p&gt;The final code is short.&lt;/p&gt;
&lt;p&gt;But the actual runtime has a rhythm 🎵&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Go down
↓
Return
↓
Swap
↓
Return
↓
Swap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once I can see that rhythm, the recursive solution feels much less magical. 🌳😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-course-schedule&quot;&gt;🎓 Course Schedule&lt;/h3&gt;
&lt;p&gt;Finally, let's look at &lt;strong&gt;Course Schedule&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This one is a little more difficult.&lt;/p&gt;
&lt;p&gt;Suppose we have three courses&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0
1
2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the prerequisites are&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 0]
[2, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;To take course 1, finish course 0 first.
To take course 2, finish course 1 first.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the dependency looks like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can we finish all courses?&lt;/p&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;p&gt;We can take them in this order &lt;code&gt;0 → 1 → 2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Easy.&lt;/p&gt;
&lt;p&gt;But what if the dependencies look like this?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1
↑   ↓
└── 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 needs 2
1 needs 0
2 needs 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everyone is waiting for someone else.&lt;/p&gt;
&lt;p&gt;We can never start.&lt;/p&gt;
&lt;p&gt;That is a &lt;strong&gt;cycle&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And if there is a cycle, we cannot finish all courses.&lt;/p&gt;
&lt;h4 id=&quot;build-the-graph&quot;&gt;Build the Graph&lt;/h4&gt;
&lt;p&gt;Here is the implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function canFinish(numCourses: number, prerequisites: number[][]): boolean {
  const graph: number[][] = Array.from({ length: numCourses }, () =&amp;gt; []);
  const indegree: number[] = Array(numCourses).fill(0);

  for (const [course, prerequisite] of prerequisites) {
    graph[prerequisite].push(course);
    indegree[course]++;
  }

  const queue: number[] = [];
  for (let course = 0; course &amp;lt; numCourses; course++) {
    if (indegree[course] === 0) queue.push(course);
  }

  let completed = 0;
  for (let head = 0; head &amp;lt; queue.length; head++) {
    const course = queue[head];
    completed++;

    for (const next of graph[course]) {
      indegree[next]--;
      if (indegree[next] === 0) queue.push(next);
    }
  }

  return completed === numCourses;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a few moving parts here.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;graph
indegree
queue
completed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is exactly the kind of algorithm where every individual line makes sense.&lt;/p&gt;
&lt;p&gt;but the whole thing can still feel confusing. 😿&lt;/p&gt;
&lt;p&gt;Let's break it down.&lt;/p&gt;
&lt;h4 id=&quot;what-is-graph-&quot;&gt;What Is &lt;code&gt;graph&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;For&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we want to know&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;After I finish this course, which courses become closer to being available?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;graph[0] = [1]
graph[1] = [2]
graph[2] = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Finish 0
↓
course 1 is affected

Finish 1
↓
course 2 is affected
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We build that here&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;graph[prerequisite].push(course);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;what-is-indegree-&quot;&gt;What Is &lt;code&gt;indegree&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;indegree&lt;/code&gt; tells us how many prerequisites a course is still waiting for.&lt;/p&gt;
&lt;p&gt;For&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;course 0: 0 prerequisites
course 1: 1 prerequisite
course 2: 1 prerequisite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree = [0, 1, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;0&lt;/code&gt; is special. Because it does not need anything before it.&lt;/p&gt;
&lt;p&gt;So we can start there immediately.&lt;/p&gt;
&lt;h4 id=&quot;start-with-courses-that-need-nothing&quot;&gt;Start With Courses That Need Nothing&lt;/h4&gt;
&lt;p&gt;We build the queue&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (let course = 0; course &amp;lt; numCourses; course++) {
  if (indegree[course] === 0) queue.push(course);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For our example&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree = [0, 1, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only course &lt;code&gt;0&lt;/code&gt; has zero prerequisites.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Course 0 is currently available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;finish-course-0&quot;&gt;Finish Course 0&lt;/h4&gt;
&lt;p&gt;Take&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;course = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;completed++;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now look at courses depending on &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;graph[0] = [1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;1&lt;/code&gt; was waiting for one prerequisite.&lt;/p&gt;
&lt;p&gt;But course &lt;code&gt;0&lt;/code&gt; is now complete.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;indegree[1]--;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[1] = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now course &lt;code&gt;1&lt;/code&gt; needs nothing.&lt;/p&gt;
&lt;p&gt;So we add it to the queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;finish-course-1&quot;&gt;Finish Course 1&lt;/h4&gt;
&lt;p&gt;Next&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;course = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;2&lt;/code&gt; depends on &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[2]: 1 → 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add it to the queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;finish-course-2&quot;&gt;Finish Course 2&lt;/h4&gt;
&lt;p&gt;Finally&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;course = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;numCourses = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;completed === numCourses; // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can finish everything! 🎉&lt;/p&gt;
&lt;h4 id=&quot;why-does-this-detect-a-cycle-&quot;&gt;Why Does This Detect a Cycle?&lt;/h4&gt;
&lt;p&gt;Now let's try&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1
↑   ↓
└── 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every course has one prerequisite.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree = [1, 1, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We try to build the initial queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (indegree[course] === 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But there is no course with indegree &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, nothing can start.&lt;/p&gt;
&lt;p&gt;Therefore&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 === 3 // false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We cannot finish the courses.&lt;/p&gt;
&lt;h4 id=&quot;another-example&quot;&gt;Another Example&lt;/h4&gt;
&lt;p&gt;Suppose&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 2
1 → 2
2 → 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;2&lt;/code&gt; needs both &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree = [0, 0, 2, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The initial queue is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finish &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[2]: 2 → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;2&lt;/code&gt; is still waiting.&lt;/p&gt;
&lt;p&gt;So don't add it yet.&lt;/p&gt;
&lt;p&gt;Finish &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[2]: 1 → 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now course &lt;code&gt;2&lt;/code&gt; is ready.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finish &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[3]: 1 → 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything can be completed.&lt;/p&gt;
&lt;p&gt;This is the key idea&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;When all prerequisites for a course are resolved, that course becomes available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;why-use-head-instead-of-shift-&quot;&gt;Why Use &lt;code&gt;head&lt;/code&gt; Instead of &lt;code&gt;shift()&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;The queue is processed like this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (let head = 0; head &amp;lt; queue.length; head++) {
  const course = queue[head]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of repeatedly doing&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;queue.shift();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we keep an index pointing to the next item to process.&lt;/p&gt;
&lt;p&gt;So the queue can grow while we iterate through it.&lt;/p&gt;
&lt;p&gt;For example&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0]

process 0
↓
queue = [0, 1]

process 1
↓
queue = [0, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;head&lt;/code&gt; simply moves forward.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
↑
head
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
    ↑
   head
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
        ↑
       head
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;complexity-5&quot;&gt;Complexity&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;V = number of courses
E = number of prerequisite relationships
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We build the graph once and process every course and edge.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time: O(V + E)
Space: O(V + E)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-5&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is probably the most interesting visualization of the three.&lt;/p&gt;
&lt;p&gt;Because there are several things changing together.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;graph
indegree
queue
head
completed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I only read&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;indegree[next]--;
if (indegree[next] === 0) queue.push(next);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I understand the syntax.&lt;/p&gt;
&lt;p&gt;But I may still ask:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why did this course become available now?&lt;/li&gt;
&lt;li&gt;Which prerequisite was removed?&lt;/li&gt;
&lt;li&gt;Why is this course still not in the queue?&lt;/li&gt;
&lt;li&gt;What does completed tell us?&lt;/li&gt;
&lt;li&gt;Where exactly does the cycle get stuck?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/704gnr0ul659berpdgng.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiY291cnNlLXNjaGVkdWxlIiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiY291cnNlLXNjaGVkdWxlIiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we view the runtime, we can watch the dependencies disappear.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2

indegree = [0, 1, 1]
queue = [0]

       ↓ finish 0

indegree = [0, 0, 1]
queue = [0, 1]

       ↓ finish 1

indegree = [0, 0, 0]
queue = [0, 1, 2]

       ↓ finish 2

completed = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code stops feeling like mysterious bookkeeping.&lt;/p&gt;
&lt;p&gt;We're really doing one simple thing:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Keep taking courses that are ready, and make their dependent courses closer to being ready.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If eventually every course becomes ready&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed === numCourses
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;there is no blocking cycle.&lt;/p&gt;
&lt;p&gt;If some courses never become ready&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed &amp;lt; numCourses
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;something is stuck in a cycle. 🎓😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-did-we-actually-learn--1&quot;&gt;🧠 What Did We Actually Learn?&lt;/h3&gt;
&lt;p&gt;These three problems look very different.&lt;/p&gt;
&lt;p&gt;But each one teaches a useful way of thinking.&lt;/p&gt;
&lt;h4 id=&quot;number-of-islands&quot;&gt;Number of Islands&lt;/h4&gt;
&lt;p&gt;When you find one part of a connected group, explore the entire group before continuing.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;What else is connected to this?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;invert-binary-tree&quot;&gt;Invert Binary Tree&lt;/h4&gt;
&lt;p&gt;Let recursive calls solve the smaller subtrees first, then transform the current node using their results.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Can my children finish their work before I change this node?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;course-schedule&quot;&gt;Course Schedule&lt;/h4&gt;
&lt;p&gt;Process things that have no unresolved dependencies, then use them to unlock more work.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;What can I safely process right now?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementations are not very long.&lt;/p&gt;
&lt;p&gt;But each one introduces a different mental model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;DFS on a grid
Recursive tree transformation
Topological sorting
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And once again, the syntax is not really the hardest part.&lt;/p&gt;
&lt;p&gt;The difficult part is keeping track of the changing state.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where are we?&lt;/li&gt;
&lt;li&gt;What changed?&lt;/li&gt;
&lt;li&gt;What is waiting?&lt;/li&gt;
&lt;li&gt;What has already been visited?&lt;/li&gt;
&lt;li&gt;Which recursive call are we inside?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes I can read every line and still lose the thread somewhere in the middle. 😿&lt;/p&gt;
&lt;p&gt;That's exactly when I want to &lt;strong&gt;view it&lt;/strong&gt;. 👀👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion-1&quot;&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we looked at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Number of Islands with recursive grid traversal&lt;/li&gt;
&lt;li&gt;Invert Binary Tree with recursion&lt;/li&gt;
&lt;li&gt;Course Schedule with topological sorting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more importantly, we followed what changed while each algorithm was running.&lt;/p&gt;
&lt;p&gt;For Number of Islands, we watched connected land disappear as it became visited.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Invert Binary Tree, we watched recursive calls go down and the tree change while they returned.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;go down
↓
come back
↓
swap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Course Schedule, we watched prerequisites disappear and new courses enter the queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree--
↓
0 prerequisites
↓
queue.push()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is exactly the kind of thing I built &lt;strong&gt;DSA View View&lt;/strong&gt; for.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app&quot;&gt;https://dsa-view-view.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can write or load a TypeScript implementation, run it with your own inputs, and move backward and forward through the runtime.&lt;/p&gt;
&lt;p&gt;If you are learning DSA too, try viewing one of these problems step by step.&lt;/p&gt;
&lt;p&gt;Especially when the implementation looks short, but your brain still says&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Wait... what just changed? 😿&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Seeing the runtime may make the idea much easier to follow.&lt;/p&gt;
&lt;p&gt;And if there is a DSA problem you want me to cover next, please let me know in the comments!&lt;/p&gt;
&lt;p&gt;I still have many algorithms to learn myself. 😸&lt;/p&gt;
&lt;p&gt;Let's train our DSA muscles together! 💪&lt;/p&gt;
&lt;p&gt;If you like DSA View View, please give it a star ⭐&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See you in the next article!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26218/learn-number-of-islands-invert-binary-tree-and-course-schedule-with-step-by-step</guid>
<pubDate>Fri, 04 Sep 2026 16:08:26 +0000</pubDate>
</item>
<item>
<title>Learn Valid Parentheses, Reverse Linked List, and Tree Max Depth with Step-by-Step Visualization in</title>
<link>https://coderlegion.com/25625/learn-valid-parentheses-reverse-linked-list-and-tree-depth-with-step-step-visualization</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer who struggles to make game sounds. 😿&lt;/p&gt;
&lt;p&gt;Have you used &lt;strong&gt;DSA View View&lt;/strong&gt; already? 👀👀&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&quot;&gt;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;DSA View View allows you to understand DSA by visualizing how your implementation actually runs.&lt;/p&gt;
&lt;p&gt;In the previous article, we looked at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two Sum&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Bubble Sort&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort-374o&quot;&gt;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort-374o&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This time, let's try three more classic problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Valid Parentheses&lt;/li&gt;
&lt;li&gt;Reverse Linked List&lt;/li&gt;
&lt;li&gt;Maximum Depth of Binary Tree&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These problems introduce three very different ideas:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Stack
Pointer manipulation
Recursion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And all three can become confusing when we only stare at the final code.&lt;/p&gt;
&lt;p&gt;So let's view what actually happens. 👀&lt;/p&gt;
&lt;p&gt;I'm still learning DSA too, so let's learn together! 😸&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/mgeyqexqh7vugtw8mia3.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-valid-parentheses&quot;&gt;🥞 Valid Parentheses&lt;/h3&gt;
&lt;p&gt;Let's start with &lt;strong&gt;Valid Parentheses&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have this string:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;()[]{}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every opening bracket has a matching closing bracket.&lt;/p&gt;
&lt;p&gt;So this is valid. ✅&lt;/p&gt;
&lt;p&gt;But this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;([)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is not valid. ❌&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Because the brackets close in the wrong order.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
  [
)
  ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;[&lt;/code&gt; should be closed before the &lt;code&gt;(&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So how can we keep track of that order?&lt;/p&gt;
&lt;h4 id=&quot;use-a-stack&quot;&gt;Use a Stack&lt;/h4&gt;
&lt;p&gt;A &lt;strong&gt;stack&lt;/strong&gt; follows a simple rule:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The last thing we put in is the first thing we take out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is called &lt;strong&gt;LIFO&lt;/strong&gt; (Last In / First Out)&lt;/p&gt;
&lt;p&gt;Imagine stacking plates.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    🍽️  ← remove first
    🍽️
    🍽️
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last plate we put on top is the first one we can take.&lt;/p&gt;
&lt;p&gt;Parentheses work in the same way.&lt;/p&gt;
&lt;p&gt;If we see:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
[
{
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then the brackets must close in reverse order&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;}
]
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So a stack is a very natural fit.&lt;/p&gt;
&lt;p&gt;Here is the implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function isValid(s: string): boolean {
  const stack: string[] = [];

  const pairs: Record&amp;lt;string, string&amp;gt; = {
    &quot;)&quot;: &quot;(&quot;,
    &quot;]&quot;: &quot;[&quot;,
    &quot;}&quot;: &quot;{&quot;,
  };

  for (const char of s) {
    if (char === &quot;(&quot; || char === &quot;[&quot; || char === &quot;{&quot;) {
      stack.push(char);
      continue;
    }

    if (stack.length === 0) return false;

    const target = stack.pop();
    if (target !== pairs[char]) {
      return false;
    }
  }

  return stack.length === 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part is what happens to &lt;code&gt;stack&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let's use&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;([])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We see&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's an opening bracket.&lt;/p&gt;
&lt;p&gt;Push it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = [&quot;(&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Push again.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = [&quot;(&quot;, &quot;[&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a closing bracket.&lt;/p&gt;
&lt;p&gt;What should it close?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And what is currently on top of the stack?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Perfect. ✅&lt;/p&gt;
&lt;p&gt;So we remove it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = [&quot;(&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It should close&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The top of the stack is also&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pop!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We reached the end with an empty stack.&lt;/p&gt;
&lt;p&gt;Valid! 🎉&lt;/p&gt;
&lt;h4 id=&quot;what-about-an-invalid-example-&quot;&gt;What About an Invalid Example?&lt;/h4&gt;
&lt;p&gt;Consider&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;([)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start the same way.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
↓
stack = [&quot;(&quot;]

[
↓
stack = [&quot;(&quot;, &quot;[&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we find&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;)&lt;/code&gt; needs&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the top of our stack is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They don't match.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;expected: (
actual:   [
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we immediately know the string is invalid.&lt;/p&gt;
&lt;h4 id=&quot;complexity-6&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;We walk through the string once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n)
Space: O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the worst case, the stack may contain all opening brackets.&lt;/p&gt;
&lt;h4 id=&quot;-let-s-view-view-it-6&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is where the stack becomes much easier to understand.&lt;/p&gt;
&lt;p&gt;When we only read.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;stack.push(char);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;stack.pop();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It can be easy to lose track of what is actually inside the stack.&lt;/p&gt;
&lt;p&gt;Especially with something like.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;({[]})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is on top right now?&lt;/p&gt;
&lt;p&gt;Which opening bracket are we trying to close?&lt;/p&gt;
&lt;p&gt;Instead of remembering everything in our head, we can follow the stack changing step by step.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/dcu4tkvrfug2d00z8e61.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidmFsaWQtcGFyZW50aGVzZXMiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidmFsaWQtcGFyZW50aGVzZXMiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Conceptually, we can see:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
↓
[(]

{
↓
[(, {]

[
↓
[(, {, []

]
↓
[(, {]

}
↓
[(]

)
↓
[]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's the whole idea.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Remember the opening brackets, and always match the most recent one first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Stack suddenly feels much less mysterious. 🥞😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-reverse-linked-list&quot;&gt;🔗 Reverse Linked List&lt;/h3&gt;
&lt;p&gt;Next, let's reverse a linked list.&lt;/p&gt;
&lt;p&gt;Suppose we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2 → 3 → 4 → 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5 → 4 → 3 → 2 → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first glance, this sounds simple.&lt;/p&gt;
&lt;p&gt;Just reverse it!&lt;/p&gt;
&lt;p&gt;But linked lists are a little different from arrays.&lt;/p&gt;
&lt;p&gt;With an array, the values live in positions like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0  1  2  3  4
↓  ↓  ↓  ↓  ↓
1  2  3  4  5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A linked list instead consists of nodes pointing to the next node.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2 → 3 → 4 → 5 → null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each arrow matters. To reverse the list, we need to reverse those arrows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 ← 2 ← 3 ← 4 ← 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this is where things can get confusing.&lt;/p&gt;
&lt;p&gt;Because if we change an arrow too early...&lt;/p&gt;
&lt;p&gt;we may lose the rest of the list. 😿&lt;/p&gt;
&lt;h4 id=&quot;three-important-variables&quot;&gt;Three Important Variables&lt;/h4&gt;
&lt;p&gt;A common iterative solution uses three variables&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev
current
next
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function reverseList(head: ListNode | null): ListNode | null {
  let prev: ListNode | null = null;
  let current = head;

  while (current !== null) {
    const next = current.next;

    current.next = prev;

    prev = current;
    current = next;
  }

  return prev;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's short.&lt;/p&gt;
&lt;p&gt;But there is a lot happening inside these few lines.&lt;/p&gt;
&lt;p&gt;Let's follow it carefully.&lt;/p&gt;
&lt;p&gt;We start with&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2 → 3 → null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev = null
current = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-1-save-the-next-node&quot;&gt;Step 1: Save the Next Node&lt;/h4&gt;
&lt;p&gt;First&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const next = current.next;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;next = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why do we need this?&lt;/p&gt;
&lt;p&gt;Because we're about to change&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we change that arrow without remembering &lt;code&gt;2&lt;/code&gt;, we lose access to the rest of the list.&lt;/p&gt;
&lt;p&gt;So first&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Save where we need to go next.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;step-2-reverse-the-arrow&quot;&gt;Step 2: Reverse the Arrow&lt;/h4&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;current.next = prev;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Originally&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But &lt;code&gt;prev&lt;/code&gt; is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first arrow has been reversed.&lt;/p&gt;
&lt;h4 id=&quot;step-3-move-prev&quot;&gt;Step 3: Move &lt;code&gt;prev&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Next&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;prev = current;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-4-move-current&quot;&gt;Step 4: Move &lt;code&gt;current&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Finally&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;current = next;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We saved &lt;code&gt;2&lt;/code&gt; earlier.&lt;/p&gt;
&lt;p&gt;So now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;current = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our state looks like this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null ← 1    2 → 3 → null
       ↑    ↑
      prev current
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we do exactly the same thing again.&lt;/p&gt;
&lt;p&gt;Save:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;next = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reverse&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2 → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev = 2
current = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null ← 1 ← 2    3 → null
           ↑    ↑
          prev  current
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One more time&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;next = null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reverse&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev = 3
current = null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null ← 1 ← 2 ← 3
               ↑
              prev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The loop stops because&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;current === null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;prev&lt;/code&gt; is the new head.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;return prev;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done! 🎉&lt;/p&gt;
&lt;h4 id=&quot;complexity-7&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;We visit each node once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n)
Space: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don't create another linked list.&lt;/p&gt;
&lt;p&gt;We only move a few pointers.&lt;/p&gt;
&lt;h4 id=&quot;-let-s-view-view-it-7&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is exactly the kind of code that I find difficult to understand by reading alone.&lt;/p&gt;
&lt;p&gt;These four lines:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const next = current.next;
current.next = prev;
prev = current;
current = next;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;look simple.&lt;/p&gt;
&lt;p&gt;But when I first see code like this, my brain starts asking.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Wait.

- Where is current now?
- Did we lose next?
- Which arrow changed?
- What exactly does prev point to?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;😿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/heoj8swgfccmhqe70pya.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHJlZS1kZXB0aCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoicmV2ZXJzZS1saXN0IiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we visualize each step, we can actually follow the pointers moving.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev      current
 ↓           ↓
null         1 → 2 → 3

      ↓↓↓

null ← 1     2 → 3
       ↑     ↑
      prev current

      ↓↓↓

null ← 1 ← 2     3
           ↑     ↑
          prev current

      ↓↓↓

null ← 1 ← 2 ← 3
               ↑
              prev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The algorithm becomes much simpler when we stop thinking of it as four mysterious assignments.&lt;/p&gt;
&lt;p&gt;It's really just&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Save next
  ↓
Reverse arrow
  ↓
Move prev
  ↓
Move current
  ↓
Repeat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nice! 🔗😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-maximum-depth-of-binary-tree&quot;&gt;🌳 Maximum Depth of Binary Tree&lt;/h3&gt;
&lt;p&gt;Finally, let's look at a tree.&lt;/p&gt;
&lt;p&gt;Consider this binary tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;        3
       / \
      9   20
         /  \
        15   7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is its maximum depth?&lt;/p&gt;
&lt;p&gt;The longest path from the root to a leaf contains three nodes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3
↓
20
↓
15
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the answer is.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How can we calculate that?&lt;/p&gt;
&lt;h4 id=&quot;think-about-a-smaller-tree&quot;&gt;Think About a Smaller Tree&lt;/h4&gt;
&lt;p&gt;Suppose we are standing at one node.&lt;/p&gt;
&lt;p&gt;We don't really need to understand the entire tree at once.&lt;/p&gt;
&lt;p&gt;We only need to ask.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;How deep is the left subtree?

How deep is the right subtree?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then choose the larger one.&lt;/p&gt;
&lt;p&gt;And add &lt;code&gt;1&lt;/code&gt; for the current node.&lt;/p&gt;
&lt;p&gt;That's exactly what this implementation does.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function maxDepth(root: TreeNode | null): number {
  if (root === null) {
    return 0;
  }

  const leftDepth = maxDepth(root.left);
  const rightDepth = maxDepth(root.right);

  return Math.max(leftDepth, rightDepth) + 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important idea is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.max(leftDepth, rightDepth) + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But recursion can feel strange.&lt;/p&gt;
&lt;p&gt;When we call&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;maxDepth(root.left);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where does the current function go?&lt;/p&gt;
&lt;p&gt;And how do all those calls eventually become one number?&lt;/p&gt;
&lt;p&gt;Let's follow a small example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
   / \
  2   3
 /
4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start at&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But before &lt;code&gt;1&lt;/code&gt; can know its depth, it asks its left child&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;maxDepth(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node &lt;code&gt;2&lt;/code&gt; asks&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;maxDepth(4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node &lt;code&gt;4&lt;/code&gt; has no children.&lt;/p&gt;
&lt;p&gt;So both sides eventually reach&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;maxDepth(null);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;returns&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore node &lt;code&gt;4&lt;/code&gt; can calculate&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;max(0, 0) + 1
= 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we return to node &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Its left side has depth&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Its right side is &lt;code&gt;null&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;max(1, 0) + 1
= 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now return to node &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Eventually its right subtree also returns&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So node &lt;code&gt;1&lt;/code&gt; gets&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;leftDepth = 2
rightDepth = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And calculates&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;max(2, 1) + 1
= 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Answer&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🎉&lt;/p&gt;
&lt;h4 id=&quot;the-interesting-part-going-down-and-coming-back-up&quot;&gt;The Interesting Part: Going Down and Coming Back Up&lt;/h4&gt;
&lt;p&gt;This is what makes recursion interesting.&lt;/p&gt;
&lt;p&gt;First, the function calls go &lt;strong&gt;down&lt;/strong&gt; the tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
↓
2
↓
4
↓
null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the answers are built while returning &lt;strong&gt;back up&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null → 0
4    → 1
2    → 2
1    → 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So recursion isn't only&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Keep calling the same function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are really two directions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Go down
  ↓
Reach the base case
  ↓
Return values back up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The base case here is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (root === null) {
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without it, the recursion would have no place to stop.&lt;/p&gt;
&lt;h4 id=&quot;complexity-8&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;Every node is visited once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time: O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The recursive call stack depends on the height of the tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Space: O(h)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where &lt;code&gt;h&lt;/code&gt; is the height of the tree.&lt;/p&gt;
&lt;p&gt;For a balanced tree, that is roughly&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;O(log n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the worst case, if the tree looks like a linked list&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
 \
  2
   \
    3
     \
      4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the depth can become&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-8&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;Recursion is probably my favorite example for visualization.&lt;/p&gt;
&lt;p&gt;Because the final implementation is tiny&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const leftDepth = maxDepth(root.left);
const rightDepth = maxDepth(root.right);

return Math.max(leftDepth, rightDepth) + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But a lot is hidden inside those function calls.&lt;/p&gt;
&lt;p&gt;When reading the code, it can feel like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;maxDepth()
inside maxDepth()
inside maxDepth()
inside maxDepth()
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where are we now? 😿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/wldufg2fqqrb9w366k5y.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHJlZS1kZXB0aCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHJlZS1kZXB0aCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we step through the execution, we can follow both parts&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Going down

1
↓
2
↓
4
↓
null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Coming back

null → 0
↓
4 → 1
↓
2 → 2
↓
1 → 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That makes the recursive idea much easier to see.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Ask the smaller subproblems for their answers, then use those answers to build the current answer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;🌳😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-did-we-actually-learn--2&quot;&gt;🧠 What Did We Actually Learn?&lt;/h3&gt;
&lt;p&gt;These three problems look completely different.&lt;/p&gt;
&lt;p&gt;But each one introduces a very useful way of thinking.&lt;/p&gt;
&lt;h4 id=&quot;valid-parentheses&quot;&gt;Valid Parentheses&lt;/h4&gt;
&lt;p&gt;Use a stack when the most recent item needs to be handled first.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;What was the last thing I opened?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;reverse-linked-list&quot;&gt;Reverse Linked List&lt;/h4&gt;
&lt;p&gt;When changing references, save what you still need before breaking the old connection.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Where do I need to go next before I change this pointer?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;maximum-depth-of-binary-tree&quot;&gt;Maximum Depth of Binary Tree&lt;/h4&gt;
&lt;p&gt;Break a problem into smaller versions of the same problem.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Can I get the answers from my children and build my answer from them?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is one reason I like learning these problems together.&lt;/p&gt;
&lt;p&gt;The implementations are not very large.&lt;/p&gt;
&lt;p&gt;But each one introduces a completely different mental model&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Stack
Pointer
Recursion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And those mental models are much harder to learn than the syntax itself.&lt;/p&gt;
&lt;p&gt;Sometimes the code tells us &lt;strong&gt;what happens&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;But I also want to see &lt;strong&gt;how it happens&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I want to view it. 👀👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion-2&quot;&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we looked at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Valid Parentheses with a stack&lt;/li&gt;
&lt;li&gt;Reverse Linked List with pointer manipulation&lt;/li&gt;
&lt;li&gt;Maximum Depth of Binary Tree with recursion&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more importantly, we followed the state while each algorithm was running.&lt;/p&gt;
&lt;p&gt;We watched change the stack.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack.push() / stack.pop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We watched move through a linked list.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev
current
next
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we watched recursive calls travel down a tree and return their answers back up.&lt;/p&gt;
&lt;p&gt;This is exactly the kind of thing I built &lt;strong&gt;DSA View View&lt;/strong&gt; for.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app&quot;&gt;https://dsa-view-view.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can write or load a TypeScript implementation, run it with your own inputs, and move backward and forward through the runtime.&lt;/p&gt;
&lt;p&gt;If you are learning DSA too, try viewing one of these problems step by step.&lt;/p&gt;
&lt;p&gt;Especially if a solution feels like&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I understand every line individually... but somehow I still don't understand the whole thing. 😿&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Seeing the runtime may connect those pieces together.&lt;/p&gt;
&lt;p&gt;And if there is a DSA problem you want me to cover next, please let me know in the comments!&lt;/p&gt;
&lt;p&gt;I still have many algorithms to learn myself. 😸&lt;/p&gt;
&lt;p&gt;Let's train our DSA muscles together! 💪&lt;/p&gt;
&lt;p&gt;If you like DSA View View, please give it a star ⭐&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See you in the next article!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25625/learn-valid-parentheses-reverse-linked-list-and-tree-depth-with-step-step-visualization</guid>
<pubDate>Fri, 28 Aug 2026 14:46:28 +0000</pubDate>
</item>
<item>
<title>1366. Rank Teams by Votes</title>
<link>https://coderlegion.com/24947/1366-rank-teams-by-votes</link>
<description>&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;br&gt;
In a special ranking system, each voter gives a rank from highest to lowest to all teams participating in the competition.&lt;/p&gt;
&lt;p&gt;The ordering of teams is decided by who received the most position-one votes. If two or more teams tie in the first position, we consider the second position to resolve the conflict, if they tie again, we continue this process until the ties are resolved. If two or more teams are still tied after considering all positions, we rank them alphabetically based on their team letter.&lt;/p&gt;
&lt;p&gt;You are given an array of strings votes which is the votes of all voters in the ranking systems. Sort all teams according to the ranking system described above.&lt;/p&gt;
&lt;p&gt;Return a string of all teams sorted by the ranking system.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: votes = [&quot;ABC&quot;,&quot;ACB&quot;,&quot;ABC&quot;,&quot;ACB&quot;,&quot;ACB&quot;]&lt;br&gt;
Output: &quot;ACB&quot;&lt;br&gt;
Explanation:&lt;br&gt;
Team A was ranked first place by 5 voters. No other team was voted as first place, so team A is the first team.&lt;br&gt;
Team B was ranked second by 2 voters and ranked third by 3 voters.&lt;br&gt;
Team C was ranked second by 3 voters and ranked third by 2 voters.&lt;br&gt;
As most of the voters ranked C second, team C is the second team, and team B is the third.&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: votes = [&quot;WXYZ&quot;,&quot;XYZW&quot;]&lt;br&gt;
Output: &quot;XWYZ&quot;&lt;br&gt;
Explanation:&lt;br&gt;
X is the winner due to the tie-breaking rule. X has the same votes as W for the first position, but X has one vote in the second position, while W does not have any votes in the second position.&lt;br&gt;
Example 3:&lt;/p&gt;
&lt;p&gt;Input: votes = [&quot;ZMNAGUEDSJYLBOPHRQICWFXTVK&quot;]&lt;br&gt;
Output: &quot;ZMNAGUEDSJYLBOPHRQICWFXTVK&quot;&lt;br&gt;
Explanation: Only one voter, so their votes are used for the ranking.&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;1 &amp;lt;= votes.length &amp;lt;= 1000&lt;br&gt;
1 &amp;lt;= votes[i].length &amp;lt;= 26&lt;br&gt;
votes[i].length == votes[j].length for 0 &amp;lt;= i, j &amp;lt; votes.length.&lt;br&gt;
votes[i][j] is an English uppercase letter.&lt;br&gt;
All characters of votes[i] are unique.&lt;br&gt;
All the characters that occur in votes[0] also occur in votes[j] where 1 &amp;lt;= j &amp;lt; votes.length.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;br&gt;
This problem can be solved using 2 data structures- a hashmap and a heap. We use the map to store the votes by each judge and the heap to sort the teams. We create a comparator to sort the teams and initialize the heap using it.&lt;br&gt;
We then traverse the votes array to calculate the votes and create the map.Once this is done we add the keys of the map which are the teams to the heap and pull the elements off one by one to get the final string.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public String rankTeams(String[] votes) {
      HashMap&amp;lt;Character,int[]&amp;gt;map=new  HashMap&amp;lt;&amp;gt;();
      String answer=&quot;&quot;;
      for(int i=0;i&amp;lt;votes.length;i++){
       String vote=votes[i];
       for(int j=0;j&amp;lt;vote.length();j++){
        char team=vote.charAt(j);
        if(!map.containsKey(team)){
         int [] counts= new int[vote.length()];
         counts[j]++;
         map.put(team,counts);
        }else{
          int [] counts =map.get(team);
          counts[j]++;
          map.replace(team,counts);
        }
       } 
      }
      PriorityQueue&amp;lt;Character&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;((a, b) -&amp;gt; {
        int[] va = map.get(a);
        int[] vb = map.get(b);
        for (int i = 0; i &amp;lt; va.length; i++) {
            if (va[i] != vb[i]) {
            return Integer.compare(vb[i], va[i]);
             }
        }
        return Character.compare(a, b);
      });  
      for (char team : map.keySet()) pq.offer(team);
      while(!pq.isEmpty()){
       char ch=pq.poll();
       answer+=ch; 
      }
      return answer;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24947/1366-rank-teams-by-votes</guid>
<pubDate>Wed, 19 Aug 2026 18:52:21 +0000</pubDate>
</item>
<item>
<title>Is Learning DSA Boring? Let's Use DSA View View 👀👀 (Two Sum, Binary Search, and Bubble Sort)</title>
<link>https://coderlegion.com/24928/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer who dislikes crowded places, so I'm planning to take a quiet vacation in September. 🏝️&lt;/p&gt;
&lt;p&gt;Have you used &lt;strong&gt;DSA View View&lt;/strong&gt; already? 👀👀&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/22758/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view&quot;&gt;https://coderlegion.com/22758/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;DSA View View allows you to understand DSA by visualizing how your implementation actually runs.&lt;/p&gt;
&lt;p&gt;But just introducing the tool is not enough.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Can it actually help us understand DSA?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this article, we'll walk through three classic problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two Sum&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Bubble Sort&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We'll first understand the algorithm, and then see what is actually happening with &lt;strong&gt;DSA View View&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/7olyo9iwv18govsyty9p.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;Let's learn together! 😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-two-sum&quot;&gt;🗺️ Two Sum&lt;/h3&gt;
&lt;p&gt;Let's start with a very famous problem.&lt;/p&gt;
&lt;p&gt;Given an array of numbers and a target value, find the indices of two numbers whose sum equals the target.&lt;/p&gt;
&lt;p&gt;For example,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums = [2, 7, 11, 15];
target = 9;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The answer is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;[0, 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2 + 7 = 9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple!&lt;/p&gt;
&lt;p&gt;So, how should we find them? 🤔&lt;/p&gt;
&lt;h4 id=&quot;brute-force&quot;&gt;Brute Force&lt;/h4&gt;
&lt;p&gt;The easiest approach is probably checking every possible pair.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function twoSum(nums: number[], target: number): number[] {
  for (let i = 0; i &amp;lt; nums.length; i++) {
    for (let j = i + 1; j &amp;lt; nums.length; j++) {
      if (nums[i] + nums[j] === target) {
        return [i, j];
      }
    }
  }

  return [];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works.&lt;/p&gt;
&lt;p&gt;But if the array becomes large, we may need to compare a lot of pairs, right?&lt;/p&gt;
&lt;p&gt;The time complexity is &lt;em&gt;O(n²)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Can we avoid checking the same values again and again?&lt;/p&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;p&gt;Let's use a &lt;code&gt;Map&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function twoSum(nums: number[], target: number): number[] {
  const seen = new Map&amp;lt;number, number&amp;gt;();

  for (let i = 0; i &amp;lt; nums.length; i++) {
    const current = nums[i];
    const need = target - current;

    if (seen.has(need)) {
      return [seen.get(need)!, i];
    }

    seen.set(current, i);
  }

  return [];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part is this 👇&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const need = target - current;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of asking&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Which two numbers should I combine?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;we ask&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;What number do I need to complete the target?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's follow the example.&lt;/p&gt;
&lt;p&gt;At first&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;current = 2
target = 9

need = 9 - 2
     = 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Have we already seen &lt;code&gt;7&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;p&gt;So we remember &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;seen = {
  2 → 0
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;current = 7
target = 9

need = 9 - 7
     = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Have we already seen &lt;code&gt;2&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Yes! 👀👀&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;seen = {
  2 → 0
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;return [0, 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done!&lt;/p&gt;
&lt;p&gt;Because we only need to walk through the array once, the time complexity becomes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n)
Space: O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-9&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;The implementation is quite small.&lt;/p&gt;
&lt;p&gt;But when I was first learning this pattern, this part still felt a little magical.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (seen.has(need))
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Where did &lt;code&gt;need&lt;/code&gt; come from?&lt;/li&gt;
&lt;li&gt;What is inside &lt;code&gt;seen&lt;/code&gt; at this moment?&lt;/li&gt;
&lt;li&gt;Why does checking the previous values solve the problem?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is exactly where visualization helps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/vlxo25nh00u0exqukl5p.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHdvLXN1bSIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHdvLXN1bSIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;With DSA View View, we can move through the runtime one step at a time and inspect how the values change.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2
↓
Need 7
↓
Remember 2
↓
7
↓
Need 2
↓
Found 2!
🎉
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the &lt;code&gt;Map&lt;/code&gt; is not just some mysterious trick.&lt;/p&gt;
&lt;p&gt;We can actually follow the idea.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Remember what we have already seen, and check whether the value we need is there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nice! 😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-binary-search&quot;&gt;🔍 Binary Search&lt;/h3&gt;
&lt;p&gt;Next is &lt;strong&gt;Binary Search&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have this sorted array&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 3, 5, 7, 9, 11, 13]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we want to find&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, we could start from &lt;code&gt;1&lt;/code&gt; and check every number.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 3 → 5 → 7 → 9 → 11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That works.&lt;/p&gt;
&lt;p&gt;But Binary Search does something smarter.&lt;/p&gt;
&lt;p&gt;Instead of checking from the beginning, it checks the &lt;strong&gt;middle&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 3, 5, 7, 9, 11, 13]
          ↑
         mid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our middle value is &lt;code&gt;7&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We are looking for &lt;code&gt;11&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;11 &amp;gt; 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the array is sorted, we already know something very useful.&lt;/p&gt;
&lt;p&gt;Everything on the left side of &lt;code&gt;7&lt;/code&gt; is also smaller than &lt;code&gt;11&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, we don't need that half anymore. 👋&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 3, 5, 7, 9, 11, 13]
             └───────┘
               search
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we check the middle of the remaining range.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[9, 11, 13]
     ↑
    mid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;11 === 11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Found it! 🎉&lt;/p&gt;
&lt;p&gt;Here is the implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function binarySearch(nums: number[], target: number): number {
  let left = 0;
  let right = nums.length - 1;

  while (left &amp;lt;= right) {
    const mid = Math.floor((left + right) / 2);

    if (nums[mid] === target) {
      return mid;
    }

    if (nums[mid] &amp;lt; target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return -1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are three important variables.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left
right
mid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They represent the current search range.&lt;/p&gt;
&lt;p&gt;For our example, they start like this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = 0
right = 6
mid = 3

[1, 3, 5, 7, 9, 11, 13]
 ↑        ↑          ↑
left     mid       right
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums[mid] &amp;lt; target;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we move &lt;code&gt;left&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;left = mid + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 3, 5, 7, 9, 11, 13]
             ↑   ↑   ↑
           left mid right
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;11&lt;/code&gt; is found.&lt;/p&gt;
&lt;h4 id=&quot;why-is-binary-search-fast-&quot;&gt;Why Is Binary Search Fast?&lt;/h4&gt;
&lt;p&gt;This is the interesting part.&lt;/p&gt;
&lt;p&gt;Each step removes about &lt;strong&gt;half of the remaining candidates&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If there are 1,000 values, we don't necessarily need 1,000 checks.&lt;/p&gt;
&lt;p&gt;It becomes roughly&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1000
↓
500
↓
250
↓
125
↓
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's why Binary Search has&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(log n)
Space: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But there is one very important condition.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The data must be sorted.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Without sorted data, we cannot safely throw away half of the search range.&lt;/p&gt;
&lt;h4 id=&quot;-let-s-view-view-it-10&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;Binary Search is one of the algorithms that made me want a visualization tool in the first place.&lt;/p&gt;
&lt;p&gt;The code itself is short&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;left = mid + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;right = mid - 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Easy.&lt;/p&gt;
&lt;p&gt;But when learning it, I sometimes found myself thinking:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Wait... which part are we searching now? 😿&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ib05yz7hx7l1ho2pmbgw.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiYmluYXJ5LXNlYXJjaCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiYmluYXJ5LXNlYXJjaCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;When we visualize &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;mid&lt;/code&gt;, and &lt;code&gt;right&lt;/code&gt;, the idea becomes much easier to follow.&lt;/p&gt;
&lt;p&gt;We are not randomly changing three numbers.&lt;/p&gt;
&lt;p&gt;We are continuously shrinking the search area.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;███████████████

       ↓

        ███████

       ↓

          ███

       ↓

           █
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's Binary Search!&lt;/p&gt;
&lt;p&gt;Cut the unnecessary half.&lt;/p&gt;
&lt;p&gt;Then cut it again. And again. And again.&lt;/p&gt;
&lt;p&gt;Until we find the answer. ✂️😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;bubble-sort&quot;&gt;Bubble Sort&lt;/h3&gt;
&lt;p&gt;Finally, let's sort something!&lt;/p&gt;
&lt;p&gt;Consider this array.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;[5, 1, 4, 2, 8];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;[1, 2, 4, 5, 8];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bubble Sort repeatedly compares &lt;strong&gt;two neighboring values&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If they are in the wrong order, it swaps them.&lt;/p&gt;
&lt;p&gt;Let's look at the beginning.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[5, 1, 4, 2, 8]
 ↑  ↑
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compare&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5 &amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So swap them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 5, 4, 2, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 5, 4, 2, 8]
    ↑  ↑
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5 &amp;gt; 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swap!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 4, 5, 2, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And continue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 4, 5, 2, 8]
       ↑  ↑

5 &amp;gt; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swap!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 4, 2, 5, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eventually, larger values move toward the end of the array.&lt;/p&gt;
&lt;p&gt;They kind of...&lt;/p&gt;
&lt;p&gt;bubble up. &lt;/p&gt;
&lt;p&gt;That's why it is called &lt;strong&gt;Bubble Sort&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here is a simple implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function bubbleSort(nums: number[]): number[] {
  for (let i = 0; i &amp;lt; nums.length - 1; i++) {
    for (let j = 0; j &amp;lt; nums.length - i - 1; j++) {
      if (nums[j] &amp;gt; nums[j + 1]) {
        [nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
      }
    }
  }

  return nums;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We repeatedly compare&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums[j];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums[j + 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and swap them when necessary.&lt;/p&gt;
&lt;p&gt;After one full pass, the largest remaining value reaches its correct position near the end.&lt;/p&gt;
&lt;p&gt;So on the next pass, we don't need to check that position again.&lt;/p&gt;
&lt;p&gt;That's why the inner loop contains.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums.length - i - 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;complexity-9&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;Bubble Sort isn't very fast for large arrays.&lt;/p&gt;
&lt;p&gt;Its time complexity is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n²)
Space: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So I probably won't suddenly replace production sorting with Bubble Sort tomorrow. 😸&lt;/p&gt;
&lt;p&gt;But as a learning example, I really like it.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Because you can &lt;strong&gt;see the algorithm working&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;-let-s-view-view-it-11&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is probably the most visually satisfying one of the three.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/7yw14rpesxucg4wdb0m7.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiYnViYmxlLXNvcnQiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiYnViYmxlLXNvcnQiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;Instead of only reading&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we can follow the values moving through the array.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[5, 1, 4, 2, 8]

 ↓ swap

[1, 5, 4, 2, 8]

    ↓ swap

[1, 4, 5, 2, 8]

       ↓ swap

[1, 4, 2, 5, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then another pass begins.&lt;/p&gt;
&lt;p&gt;The code contains nested loops, indexes, comparisons, and swaps.&lt;/p&gt;
&lt;p&gt;But visually, the basic rule is extremely simple:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Compare neighbors. If the left one is bigger, swap them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Repeat. Repeat. Repeat.&lt;/p&gt;
&lt;p&gt;Sorted! 🎉&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-did-we-actually-learn--3&quot;&gt;🧠 What Did We Actually Learn?&lt;/h3&gt;
&lt;p&gt;These three problems look quite different.&lt;/p&gt;
&lt;p&gt;But each one introduces a useful way of thinking.&lt;/p&gt;
&lt;h4 id=&quot;two-sum&quot;&gt;Two Sum&lt;/h4&gt;
&lt;p&gt;Remember information from previous steps.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Have I already seen what I need?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;binary-search&quot;&gt;Binary Search&lt;/h4&gt;
&lt;p&gt;Use what we already know to remove impossible candidates.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Can I safely discard half of the search space?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;bubble-sort-1&quot;&gt;Bubble Sort&lt;/h4&gt;
&lt;p&gt;Break a larger problem into many small comparisons.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Are these two values in the correct order?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is one of the things I find interesting about learning DSA.&lt;/p&gt;
&lt;p&gt;At first, the implementation can look like a collection of indexes, loops, conditions, and mysterious variables.&lt;/p&gt;
&lt;p&gt;But behind the code, there is usually a much simpler idea.&lt;/p&gt;
&lt;p&gt;And sometimes I don't fully understand that idea just by staring at the code.&lt;/p&gt;
&lt;p&gt;I want to &lt;strong&gt;view it&lt;/strong&gt;. 👀👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion-3&quot;&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we looked at three classic algorithms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two Sum with a &lt;code&gt;Map&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Bubble Sort&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more importantly, we looked at &lt;strong&gt;how the data changes while they run&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I think this is where visualization can be especially useful.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reading the final implementation tells us &lt;strong&gt;what the code is&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Stepping through it helps us understand &lt;strong&gt;why it works&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's exactly why I built DSA View View.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app&quot;&gt;https://dsa-view-view.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can write or load a TypeScript implementation, run it with your own inputs, and move backward and forward through the runtime.&lt;/p&gt;
&lt;p&gt;If you are learning DSA too, try taking a problem you already solved and viewing it step by step.&lt;/p&gt;
&lt;p&gt;You may notice something you didn't notice when only reading the code. 👀&lt;/p&gt;
&lt;p&gt;And if there is a DSA problem you want me to cover next, please let me know in the comments!&lt;/p&gt;
&lt;p&gt;I still have many algorithms to learn myself. 😸&lt;/p&gt;
&lt;p&gt;Let's train our DSA muscles together! 💪&lt;/p&gt;
&lt;p&gt;If you like DSA View View, please give it a star ⭐&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And my DSA View View has launched at TinyLaunch! 🚀 Please take a loot 👇&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.tinylaunch.com/launch/17804-dsa-view-view&quot;&gt;https://www.tinylaunch.com/launch/17804-dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See you in the next article!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24928/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort</guid>
<pubDate>Wed, 19 Aug 2026 15:14:55 +0000</pubDate>
</item>
<item>
<title>768. Max Chunks To Make Sorted II</title>
<link>https://coderlegion.com/24400/768-max-chunks-to-make-sorted-ii</link>
<description>&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;br&gt;
You are given an integer array arr.&lt;/p&gt;
&lt;p&gt;We split arr into some number of chunks (i.e., partitions), and individually sort each chunk. After concatenating them, the result should equal the sorted array.&lt;/p&gt;
&lt;p&gt;Return the largest number of chunks we can make to sort the array.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: arr = [5,4,3,2,1]&lt;br&gt;
Output: 1&lt;br&gt;
Explanation:&lt;br&gt;
Splitting into two or more chunks will not return the required result.&lt;br&gt;
For example, splitting into [5, 4], [3, 2, 1] will result in [4, 5, 1, 2, 3], which isn't sorted.&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: arr = [2,1,3,4,4]&lt;br&gt;
Output: 4&lt;br&gt;
Explanation:&lt;br&gt;
We can split into two chunks, such as [2, 1], [3, 4, 4].&lt;br&gt;
However, splitting into [2, 1], [3], [4], [4] is the highest number of chunks possible.&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;1 &amp;lt;= arr.length &amp;lt;= 2000&lt;br&gt;
0 &amp;lt;= arr[i] &amp;lt;= 108&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This can be solved by traversing the array twice. The first time we put the elements in a min-heap/priority queue. This ensures that we have a list of the elements sorted in ascending order. Then we create 3 integer variables. One stores the sum of the sorted elements in the heap and is named sum. The second stores the total of the elements traversed in the array and is named total. The third stores the maximum number of chunks and is named answer.&lt;/p&gt;
&lt;p&gt;We then traverse the array for the second time. We pull the topmost element from the heap and add it to sum. We add the element at the current index in the array and add to total. If the values are equal, we increment answer. Once we have traversed the array, we return the value of answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public int maxChunksToSorted(int[] arr) {
     PriorityQueue&amp;lt;Integer&amp;gt; pq=new PriorityQueue&amp;lt;&amp;gt;();
     int total=0;
     int sum=0;
     int answer=0;
     for(int i=0;i&amp;lt;arr.length;i++)pq.offer(arr[i]);
     for(int i=0;i&amp;lt;arr.length;i++){
      sum+=pq.poll();
      total+=arr[i];
      if(sum==total)answer++;  
     }
     return answer;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24400/768-max-chunks-to-make-sorted-ii</guid>
<pubDate>Mon, 10 Aug 2026 17:51:40 +0000</pubDate>
</item>
<item>
<title>Leetcode #324. Wiggle Sort II</title>
<link>https://coderlegion.com/24112/leetcode-324-wiggle-sort-ii</link>
<description>&lt;p&gt;&lt;em&gt;Greedy algorithms are usually given in leetcode style technical interviews.They involve taking the maximum or minimum.Below is a an example of using the greedy approach to solve a problem.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Given an integer array nums, reorder it such that nums[0] &amp;lt; nums[1] &amp;gt; nums[2] &amp;lt; nums[3]....&lt;/p&gt;
&lt;p&gt;You may assume the input array always has a valid answer.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: nums = [1,5,1,1,6,4]&lt;br&gt;
Output: [1,6,1,5,1,4]&lt;br&gt;
Explanation: [1,4,1,5,1,6] is also accepted.&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: nums = [1,3,2,2,3,1]&lt;br&gt;
Output: [2,3,1,3,1,2]&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;.1 &amp;lt;= nums.length &amp;lt;= 5 * 104&lt;br&gt;
.0 &amp;lt;= nums[i] &amp;lt;= 5000&lt;br&gt;
.It is guaranteed that there will be an answer for the given input nums.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This algorithm can be solved using the greedy approach.We copy the contents of the original array into a new one and sort it.We create two pointers. One is placed at the end of the new array,the other in the middle of it.We iterate through the original from the start.If the index is even,we take the value from the second pointer.Otherwise,we take the value from the first one.This ensures that the biggest values are always placed at the odd indexes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public void wiggleSort(int[] nums) {
     int [] nums1= new int [nums.length];
     for(int i=0;i&amp;lt;nums.length;i++)nums1[i]=nums[i];
     Arrays.sort(nums1);
     int nums1Index=nums.length-1;
     int nums1Index1=(nums.length-1)/2;      
     for(int i=0;i&amp;lt;nums.length;i++){
      if(i%2==0)nums[i]=nums1[nums1Index1--];
      else nums[i]=nums1[nums1Index--];  
     }
     return;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C#&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Solution {
    public void WiggleSort(int[] nums) {
     int [] nums1= new int [nums.Length];
     for(int i=0;i&amp;lt;nums.Length;i++)nums1[i]=nums[i];
     Array.Sort(nums1);
     int nums1Index=nums.Length-1;
     int nums1Index1=(nums.Length-1)/2;      
     for(int i=0;i&amp;lt;nums.Length;i++){
      if(i%2==0)nums[i]=nums1[nums1Index1--];
      else nums[i]=nums1[nums1Index--];  
     }
     return;   
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C++&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    void wiggleSort(vector&amp;lt;int&amp;gt;&amp;amp; nums) {
     vector&amp;lt;int&amp;gt; nums1;
     for(int i=0;i&amp;lt;nums.size();i++)nums1.push_back(nums[i]);
     std::sort(nums1.begin(),nums1.end());
     int nums1Index=nums.size()-1;
     int nums1Index1=(nums.size()-1)/2;      
     for(int i=0;i&amp;lt;nums.size();i++){
      if(i%2==0)nums[i]=nums1[nums1Index1--];
      else nums[i]=nums1[nums1Index--];  
     }
     return;   
    }
};
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24112/leetcode-324-wiggle-sort-ii</guid>
<pubDate>Tue, 04 Aug 2026 19:55:27 +0000</pubDate>
</item>
<item>
<title>Leetcode # 107. Binary Tree Level Order Traversal II</title>
<link>https://coderlegion.com/24110/leetcode-107-binary-tree-level-order-traversal-ii</link>
<description>&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Given the root of a binary tree, return the bottom-up level order traversal of its nodes' values. (i.e., from left to right, level by level from leaf to root).&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: root = [3,9,20,null,null,15,7]&lt;br&gt;
Output: [[15,7],[9,20],[3]]&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: root = [1]&lt;br&gt;
Output: [[1]]&lt;br&gt;
Example 3:&lt;/p&gt;
&lt;p&gt;Input: root = []&lt;br&gt;
Output: []&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;.The number of nodes in the tree is in the range [0, 2000].&lt;br&gt;
.-1000 &amp;lt;= Node.val &amp;lt;= 1000&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Using a recursive function that returns a hashmap, a hashmap of the entire tree from top to bottom was obtained and a list was built. This list was reversed and returned.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
     public HashMap&amp;lt;Integer,List&amp;lt;Integer&amp;gt;&amp;gt; getRestOfTree(int level,TreeNode t){
     int value= t.val;
     List&amp;lt;Integer&amp;gt; root= new ArrayList&amp;lt;&amp;gt;();
     root.add(value);
     HashMap&amp;lt;Integer,List&amp;lt;Integer&amp;gt;&amp;gt; tree= new HashMap&amp;lt;&amp;gt;();
     tree.put(level,root);
     if(t.left ==null &amp;amp;&amp;amp; t.right== null){
       return tree;
     }else{
      int nextlevel =level+1;
      if(t.left==null){
         HashMap&amp;lt;Integer,List&amp;lt;Integer&amp;gt;&amp;gt; rightSubtree= getRestOfTree(nextlevel,t.right);
         tree.putAll(rightSubtree);
         return tree;
      }else if(t.right==null){
          HashMap&amp;lt;Integer,List&amp;lt;Integer&amp;gt;&amp;gt; leftSubtree= getRestOfTree(nextlevel,t.left);
         tree.putAll(leftSubtree);
         return tree;
      }else{
         HashMap&amp;lt;Integer,List&amp;lt;Integer&amp;gt;&amp;gt; leftSubtree= getRestOfTree(nextlevel, t.left);
         HashMap&amp;lt;Integer,List&amp;lt;Integer&amp;gt;&amp;gt; rightSubtree= getRestOfTree(nextlevel, t.right);
         HashMap&amp;lt;Integer,List&amp;lt;Integer&amp;gt;&amp;gt; restSubtree = new HashMap&amp;lt;&amp;gt;();
         for(Integer key:leftSubtree.keySet()){
           if(rightSubtree.containsKey(key)){
             List&amp;lt;Integer&amp;gt; rightSubtreeList = rightSubtree.get(key);
             List&amp;lt;Integer&amp;gt; leftSubtreeList= leftSubtree.get(key);
             leftSubtreeList.addAll(rightSubtreeList);
             leftSubtree.replace(key,leftSubtreeList);
           }
         }
         for(Integer key:rightSubtree.keySet()){
            if(!leftSubtree.containsKey(key)){
               List&amp;lt;Integer&amp;gt; rightSubtreeList= rightSubtree.get(key);
               restSubtree.put(key,rightSubtreeList);
            }
         }
         tree.putAll(leftSubtree);
         tree.putAll(restSubtree);
         return tree;
      }
      }
   }
    public List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; levelOrderBottom(TreeNode root) {
        if(root==null)return new ArrayList&amp;lt;&amp;gt;();
        HashMap&amp;lt;Integer,List&amp;lt;Integer&amp;gt;&amp;gt; tree = getRestOfTree(1,root);
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; values= new ArrayList&amp;lt;&amp;gt;();
        for(Integer key: tree.keySet()){
         List&amp;lt;Integer&amp;gt; nodes= tree.get(key);
         values.add(nodes);
        }
        Collections.reverse(values);
        return values;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24110/leetcode-107-binary-tree-level-order-traversal-ii</guid>
<pubDate>Tue, 04 Aug 2026 19:48:53 +0000</pubDate>
</item>
<item>
<title>Mastering the Two Pointers Technique in Python (With Easy Examples)</title>
<link>https://coderlegion.com/24099/mastering-the-two-pointers-technique-in-python-with-easy-examples</link>
<description>&lt;h2 id=&quot;mastering-the-two-pointers-technique-in-python-with-easy-examples-&quot;&gt;Mastering the Two Pointers Technique in Python (With Easy Examples)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; Python • Programming • DSA • Algorithms&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; Annet A&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;When I first started solving coding problems, I noticed that many array and string questions looked different but could actually be solved using the same approach: &lt;strong&gt;Two Pointers&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead of using nested loops and checking every possible pair of elements, the two pointers technique allows us to solve many problems efficiently in &lt;strong&gt;O(n)&lt;/strong&gt; time.&lt;/p&gt;
&lt;p&gt;In this blog, I'll explain what the Two Pointers technique is, when to use it, and some common patterns with examples.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-the-two-pointers-technique-&quot;&gt;What is the Two Pointers Technique?&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Two Pointers&lt;/strong&gt; technique uses &lt;strong&gt;two indices (pointers)&lt;/strong&gt; that move through a data structure such as an array or string.&lt;/p&gt;
&lt;p&gt;Instead of processing one element at a time, we use two positions that move based on certain conditions until we reach the desired result.&lt;/p&gt;
&lt;p&gt;This often reduces the time complexity from &lt;strong&gt;O(n²)&lt;/strong&gt; to &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-should-you-use-two-pointers-&quot;&gt;When Should You Use Two Pointers?&lt;/h2&gt;
&lt;p&gt;The technique is useful when working with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ Sorted arrays&lt;/li&gt;
&lt;li&gt;✅ Strings&lt;/li&gt;
&lt;li&gt;✅ Palindromes&lt;/li&gt;
&lt;li&gt;✅ Removing duplicates&lt;/li&gt;
&lt;li&gt;✅ Pair sum problems&lt;/li&gt;
&lt;li&gt;✅ Reversing arrays&lt;/li&gt;
&lt;li&gt;✅ Sliding window variations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whenever you see words like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pair&lt;/li&gt;
&lt;li&gt;Sorted&lt;/li&gt;
&lt;li&gt;Remove duplicates&lt;/li&gt;
&lt;li&gt;Reverse&lt;/li&gt;
&lt;li&gt;Palindrome&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;think about whether the Two Pointers approach can simplify the solution.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;types-of-two-pointer-patterns&quot;&gt;Types of Two Pointer Patterns&lt;/h2&gt;
&lt;p&gt;There are several common patterns.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-opposite-direction-pointers&quot;&gt;1. Opposite Direction Pointers&lt;/h3&gt;
&lt;p&gt;One pointer starts from the beginning and the other starts from the end.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Left                          Right
 ↓                              ↓

[1, 2, 3, 4, 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The pointers move toward each other.&lt;/p&gt;
&lt;h4 id=&quot;common-problems&quot;&gt;Common Problems&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Valid Palindrome&lt;/li&gt;
&lt;li&gt;Two Sum II&lt;/li&gt;
&lt;li&gt;Reverse String&lt;/li&gt;
&lt;li&gt;Container With Most Water&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;example&quot;&gt;Example&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;s = list(&quot;hello&quot;)

left = 0
right = len(s) - 1

while left &amp;lt; right:
    s[left], s[right] = s[right], s[left]
    left += 1
    right -= 1

print(&quot;&quot;.join(s))
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;output&quot;&gt;Output&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;olleh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Time Complexity:&lt;/strong&gt; &lt;code&gt;O(n)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Space Complexity:&lt;/strong&gt; &lt;code&gt;O(1)&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-same-direction-pointers-fast-and-slow-&quot;&gt;2. Same Direction Pointers (Fast and Slow)&lt;/h3&gt;
&lt;p&gt;Both pointers move from left to right.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;fast pointer&lt;/strong&gt; explores every element while the &lt;strong&gt;slow pointer&lt;/strong&gt; keeps track of the correct position.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Slow   Fast
 ↓      ↓

[1, 1, 2, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;common-problems-1&quot;&gt;Common Problems&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Remove Duplicates from Sorted Array&lt;/li&gt;
&lt;li&gt;Move Zeroes&lt;/li&gt;
&lt;li&gt;Remove Element&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;example-1&quot;&gt;Example&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;nums = [1, 1, 2, 2, 3]

left = 0

for right in range(1, len(nums)):
    if nums[left] != nums[right]:
        left += 1
        nums[left] = nums[right]

print(nums[:left + 1])
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;output-1&quot;&gt;Output&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Time Complexity:&lt;/strong&gt; &lt;code&gt;O(n)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Space Complexity:&lt;/strong&gt; &lt;code&gt;O(1)&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-slow-and-fast-pointer&quot;&gt;3. Slow and Fast Pointer&lt;/h3&gt;
&lt;p&gt;This is another variation where one pointer moves slower than the other.&lt;/p&gt;
&lt;p&gt;Usually,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;slow += 1
fast += 2
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;common-problems-2&quot;&gt;Common Problems&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Detect Cycle in Linked List&lt;/li&gt;
&lt;li&gt;Find Middle of Linked List&lt;/li&gt;
&lt;li&gt;Happy Number&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This pattern is primarily used with &lt;strong&gt;Linked Lists&lt;/strong&gt; rather than arrays.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;example-two-sum-ii&quot;&gt;Example: Two Sum II&lt;/h2&gt;
&lt;p&gt;Given a &lt;strong&gt;sorted array&lt;/strong&gt;, find two numbers whose sum equals the target.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;numbers = [2, 7, 11, 15]
target = 9

left = 0
right = len(numbers) - 1

while left &amp;lt; right:
    current = numbers[left] + numbers[right]

    if current == target:
        print(left, right)
        break
    elif current &amp;lt; target:
        left += 1
    else:
        right -= 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;output-2&quot;&gt;Output&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how we never use nested loops.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;advantages&quot;&gt;Advantages&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✅ Easy to understand&lt;/li&gt;
&lt;li&gt;✅ Reduces time complexity&lt;/li&gt;
&lt;li&gt;✅ Often converts &lt;strong&gt;O(n²)&lt;/strong&gt; solutions into &lt;strong&gt;O(n)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;✅ Uses constant extra space&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;limitations&quot;&gt;Limitations&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Works best with sorted data.&lt;/li&gt;
&lt;li&gt;Doesn't apply to every array problem.&lt;/li&gt;
&lt;li&gt;Choosing pointer movement correctly is important.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;tips-to-identify-two-pointer-problems&quot;&gt;Tips to Identify Two Pointer Problems&lt;/h2&gt;
&lt;p&gt;Ask yourself these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is the array sorted?&lt;/li&gt;
&lt;li&gt;Am I searching for a pair?&lt;/li&gt;
&lt;li&gt;Can I avoid nested loops?&lt;/li&gt;
&lt;li&gt;Can I process elements from both ends?&lt;/li&gt;
&lt;li&gt;Can one pointer track the answer while another explores?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the answer is &lt;strong&gt;Yes&lt;/strong&gt;, the Two Pointers technique is worth considering.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;The Two Pointers technique is one of the most important problem-solving patterns in Data Structures and Algorithms. Once you understand &lt;strong&gt;when&lt;/strong&gt; and &lt;strong&gt;how&lt;/strong&gt; to move the pointers, many coding interview problems become much easier.&lt;/p&gt;
&lt;p&gt;I recently practiced this technique by solving problems like &lt;strong&gt;Remove Duplicates from Sorted Array&lt;/strong&gt;, &lt;strong&gt;Merge Sorted Array&lt;/strong&gt;, &lt;strong&gt;Valid Palindrome&lt;/strong&gt;, and &lt;strong&gt;Two Sum II&lt;/strong&gt;. Each problem helped me recognize different pointer movement patterns and improved my confidence in solving array and string problems efficiently.&lt;/p&gt;
&lt;p&gt;Mastering this technique is a great step before learning more advanced patterns like &lt;strong&gt;Sliding Window&lt;/strong&gt;, which builds upon similar ideas while handling subarrays and substrings.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;⭐ If you found this article helpful, consider sharing it with fellow developers.&lt;/p&gt;
&lt;p&gt;Happy Coding! 🚀&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;— Annet A&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24099/mastering-the-two-pointers-technique-in-python-with-easy-examples</guid>
<pubDate>Tue, 04 Aug 2026 17:55:59 +0000</pubDate>
</item>
<item>
<title>How I Made JavaScript Execution Visual and Rewindable (DSA View View 👀👀)</title>
<link>https://coderlegion.com/23738/how-i-made-javascript-execution-visual-and-rewindable-dsa-view-view</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I'm &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer who was recently &lt;strong&gt;knocked off my feet by how good the zeetong (sole) from a Dutch fish shop was&lt;/strong&gt;. 🐟😸&lt;/p&gt;
&lt;p&gt;In the previous article, I explained how DSA View View takes the &lt;code&gt;TypeScript&lt;/code&gt; written by a user, analyzes the function or class signature, and generates a Verification form.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/how-i-built-the-editor-and-compile-pipeline-for-a-typescript-dsa-visualizer-dsa-view-view--530f&quot;&gt;https://dev.to/nyaomaru/how-i-built-the-editor-and-compile-pipeline-for-a-typescript-dsa-visualizer-dsa-view-view--530f&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This time, we are entering the heart of DSA View View: the &lt;strong&gt;Runtime&lt;/strong&gt; and &lt;strong&gt;Visualization&lt;/strong&gt; layers.&lt;/p&gt;
&lt;p&gt;If you call an ordinary JavaScript function, the direct result you get back is usually the final return value.&lt;/p&gt;
&lt;p&gt;But when I am studying DSA, that is not the part I really want to see.&lt;/p&gt;
&lt;p&gt;I want to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What happened in the middle of the loop?&lt;/li&gt;
&lt;li&gt;When did the pointer move?&lt;/li&gt;
&lt;li&gt;At which exact moment did the variable change?&lt;/li&gt;
&lt;li&gt;How deep did the recursion go?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words, I want to see &lt;strong&gt;the journey to the answer&lt;/strong&gt;, not only the answer itself.&lt;/p&gt;
&lt;p&gt;JavaScript, unfortunately, does not provide an API that accepts:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Excuse me, could you move the current line one step backward?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So how does DSA View View rewind execution?&lt;/p&gt;
&lt;p&gt;Here is the answer up front. 😸&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Before execution, DSA View View rewrites the code into a version that can record its intermediate state, then runs it until it completes or reaches a configured limit.&lt;/p&gt;
&lt;p&gt;The UI then plays those recorded states forward and backward like security-camera footage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this article, we will look at that &lt;strong&gt;record-and-replay&lt;/strong&gt; system through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AST transformation with Babel&lt;/li&gt;
&lt;li&gt;Injecting &lt;code&gt;recordStep&lt;/code&gt; calls&lt;/li&gt;
&lt;li&gt;Preserving the original meaning of the code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExecutionStep&lt;/code&gt; and snapshot patches&lt;/li&gt;
&lt;li&gt;Rewinding and jumping to variable changes&lt;/li&gt;
&lt;li&gt;Running code inside a Web Worker&lt;/li&gt;
&lt;li&gt;Detecting the appropriate Visualization&lt;/li&gt;
&lt;li&gt;Tests that protect the AST transformation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The source code is available here 👇&lt;/p&gt;
&lt;p&gt;embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;All right, let’s open the Runtime and see what Mr. View has been watching. 👀👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-the-big-picture-turning-execution-into-an-array-of-steps&quot;&gt;📝 The Big Picture: Turning Execution into an Array of Steps&lt;/h3&gt;
&lt;p&gt;The Runtime pipeline looks roughly like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;User TypeScript
      ↓
Prepare common DSA classes
      ↓
Parse the code into an AST with Babel Parser
      ↓
Visitors inject recordStep(...)
      ↓
Transform TypeScript into JavaScript with Babel
      ↓
Execute it with new Function inside a Web Worker
      ↓
Generate ExecutionStep[]
      ↓
React moves currentStep forward and backward
      ↓
Display the Visualization that matches the trace
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most important design decision here is that &lt;strong&gt;the user’s code is not connected directly to a React component&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead, &lt;code&gt;ExecutionStep[]&lt;/code&gt; acts as a shared recording format.&lt;/p&gt;
&lt;p&gt;That lets arrays, pointers, recursion, trees, heaps, and other structures all live on the same timeline.&lt;/p&gt;
&lt;p&gt;Let’s walk through how that works.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-runtime-planting-observation-points-in-the-code&quot;&gt;🔭 Runtime: Planting Observation Points in the Code&lt;/h3&gt;
&lt;p&gt;This is the heart of DSA View View. 💖&lt;/p&gt;
&lt;p&gt;The Runtime needs intermediate states, so it does not execute the user’s code exactly as written.&lt;/p&gt;
&lt;p&gt;First, Babel transforms the AST and injects calls to &lt;code&gt;recordStep&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;injecting-recordstep-into-the-ast&quot;&gt;Injecting &lt;code&gt;recordStep&lt;/code&gt; into the AST&lt;/h4&gt;
&lt;p&gt;Suppose the user writes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;let left = 0;

while (left &amp;lt; right) {
  left++;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DSA View View parses it into an AST and transforms it into something roughly like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;let left = 0;
recordStep(&quot;variable-declaration&quot;, 1, &quot;let left = 0&quot;, { left });

while (left &amp;lt; right) {
  recordStep(&quot;loop-iteration&quot;, 3, &quot;while (left &amp;lt; right)&quot;, { left, right });

  left++;
  recordStep(&quot;assignment&quot;, 4, &quot;left++&quot;, { left, right });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;&lt;p&gt;The second argument to &lt;code&gt;recordStep&lt;/code&gt; is the original source line number, not the execution-step number.&lt;br&gt;
Because line 2 is empty, the recorded lines are &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;3&lt;/code&gt;, and &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the real implementation, dedicated visitors handle AST nodes such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FunctionDeclaration&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FunctionExpression&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ArrowFunctionExpression&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ClassMethod&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VariableDeclaration&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AssignmentExpression&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UpdateExpression&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CallExpression&lt;/code&gt; nodes that mutate arrays&lt;/li&gt;
&lt;li&gt;&lt;code&gt;for&lt;/code&gt; / &lt;code&gt;while&lt;/code&gt; / &lt;code&gt;for...of&lt;/code&gt; / &lt;code&gt;for...in&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReturnStatement&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A step can therefore be recorded when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A function is entered&lt;/li&gt;
&lt;li&gt;A variable is declared&lt;/li&gt;
&lt;li&gt;A loop completes an iteration&lt;/li&gt;
&lt;li&gt;A value is assigned&lt;/li&gt;
&lt;li&gt;An array is mutated&lt;/li&gt;
&lt;li&gt;A function returns&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Current limitation&lt;/strong&gt;&lt;br&gt;
callback bodies inside methods such as &lt;code&gt;sort&lt;/code&gt;, &lt;code&gt;reduce&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, and &lt;code&gt;forEach&lt;/code&gt; are intentionally excluded from instrumentation for now.&lt;/p&gt;
&lt;p&gt;The goal is not to trace every corner of JavaScript immediately. I am starting with patterns that are useful for DSA traces and can be instrumented without breaking the original behavior.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;injecting-the-code-is-easier-than-not-breaking-it&quot;&gt;Injecting the Code Is Easier Than Not Breaking It&lt;/h4&gt;
&lt;p&gt;The hardest part of the AST transformation was not calling &lt;code&gt;recordStep&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The hard part was making sure that,&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;after adding the recording logic, every original expression is still evaluated the same number of times and still returns the same value.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For example, if a return expression were evaluated once for recording and once for the actual return, its side effects would run twice.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;return someFunction();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the transformed code first stores the value in a temporary variable.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const algorithmVisualizerReturnValue = someFunction();

recordStep(&quot;return&quot;, line, description, {
  &quot;return value&quot;: algorithmVisualizerReturnValue,
  &quot;return location&quot;: returnLocation,
});

return algorithmVisualizerReturnValue;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assignment expressions are another fun little trap.&lt;/p&gt;
&lt;p&gt;Consider this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const result = (arr[i++] = 7);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The transformed code must preserve all three facts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i++&lt;/code&gt; runs exactly once&lt;/li&gt;
&lt;li&gt;The pre-increment value of &lt;code&gt;i&lt;/code&gt; is used as the array index&lt;/li&gt;
&lt;li&gt;The assignment expression itself evaluates to &lt;code&gt;7&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DSA View View wraps the assignment in a synthetic arrow function, stores the result in a temporary variable, records the step, and returns the same value.&lt;/p&gt;
&lt;p&gt;Using an arrow function also avoids accidentally stealing the surrounding &lt;code&gt;this&lt;/code&gt; or &lt;code&gt;arguments&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;AST transformation cannot be &quot;close enough&quot;.&lt;/p&gt;
&lt;p&gt;If adding more visible steps changes the answer, the visualizer has somehow become the least trustworthy character in the story.&lt;/p&gt;
&lt;p&gt;Not ideal.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-executionstep-making-execution-replayable&quot;&gt;🖍️ &lt;code&gt;ExecutionStep&lt;/code&gt;: Making Execution Replayable&lt;/h3&gt;
&lt;p&gt;The Runtime records each observed state as an &lt;code&gt;ExecutionStep&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A shortened version of the real type looks like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;type ExecutionStep = {
  stepNumber: number;
  type: ExecutionStepType;
  line: number;
  column?: number;
  description: string;
  variables: Record&amp;lt;string, unknown&amp;gt;;
  timestamp: number;
  callStack?: string[];
  scope?: string;
  metadata?: {
    loopIteration?: number;
    conditionResult?: boolean;
    functionName?: string;
    heapTrace?: HeapTraceSnapshot;
  };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Runtime currently generates steps including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;function-call&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;function-entry&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;variable-declaration&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;assignment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array-mutation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;loop-iteration&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;return&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;line&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; are used in the timeline.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;variables&lt;/code&gt; provides the data needed to reconstruct the UI at that moment.&lt;/p&gt;
&lt;p&gt;Recursive traces also use &lt;code&gt;callStack&lt;/code&gt;, while prepared heap implementations can attach &lt;code&gt;metadata.heapTrace&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;saving-references-would-rewrite-the-past&quot;&gt;Saving References Would Rewrite the Past&lt;/h4&gt;
&lt;p&gt;Suppose an array reference were stored directly inside a step.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const stack = [0, 1];
recordStep(/* save stack */);

stack.pop();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the recorded step keeps the same reference, the earlier state will also appear as &lt;code&gt;[0]&lt;/code&gt; after &lt;code&gt;pop()&lt;/code&gt; runs.&lt;/p&gt;
&lt;p&gt;That is not a historical record.&lt;/p&gt;
&lt;p&gt;That is the past quietly editing itself.&lt;/p&gt;
&lt;p&gt;So values passed to &lt;code&gt;recordStep&lt;/code&gt; are deep-cloned at that moment.&lt;/p&gt;
&lt;p&gt;The Runtime normally uses &lt;code&gt;structuredClone&lt;/code&gt;. When it encounters values that need different handling, it falls back to a cloning implementation that supports cycles, &lt;code&gt;Map&lt;/code&gt;, &lt;code&gt;Set&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;RegExp&lt;/code&gt;, and other cases.&lt;/p&gt;
&lt;h4 id=&quot;the-delta-snapshot-is-really-more-like-a-patch&quot;&gt;The &quot;Delta Snapshot&quot; Is Really More Like a Patch&lt;/h4&gt;
&lt;p&gt;The implementation contains a name called &lt;code&gt;variableDeltas&lt;/code&gt;, but this deserves a more precise explanation.&lt;/p&gt;
&lt;p&gt;The current Runtime does &lt;strong&gt;not&lt;/strong&gt; compare every value with the previous state and store only the keys that truly changed.&lt;/p&gt;
&lt;p&gt;Instead:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first step stores the initial state, including the inputs&lt;/li&gt;
&lt;li&gt;Later steps deep-clone the visible variables passed to that &lt;code&gt;recordStep&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;When a full snapshot is needed, the patches are merged in order&lt;/li&gt;
&lt;li&gt;Reconstructed snapshots are cached&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So, strictly speaking, these are closer to &lt;strong&gt;per-step patches&lt;/strong&gt; than minimal deltas.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Step 0 patch: { left: 0, right: 5 }
Step 1 patch: { left: 1 }
Step 2 patch: { right: 4 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reconstructing them produces.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Step 0 snapshot: { left: 0, right: 5 }
Step 1 snapshot: { left: 1, right: 5 }
Step 2 snapshot: { left: 1, right: 4 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;variables&lt;/code&gt; property of each &lt;code&gt;ExecutionStep&lt;/code&gt; is a getter.&lt;/p&gt;
&lt;p&gt;It starts from a previously cached snapshot and merges only the patches needed to reach the requested step.&lt;/p&gt;
&lt;p&gt;There is one more implementation detail worth mentioning.&lt;/p&gt;
&lt;p&gt;The Runtime sends state from the Worker to the UI with &lt;code&gt;postMessage&lt;/code&gt;. During that structured clone, reading the getter also materializes the snapshot.&lt;/p&gt;
&lt;p&gt;So the data does not remain a tiny delta forever and magically arrive on the main thread in that form.&lt;/p&gt;
&lt;p&gt;The design mainly reduces eager reconstruction and cloning &lt;strong&gt;while the execution is being recorded&lt;/strong&gt;. 📽️&lt;/p&gt;
&lt;h4 id=&quot;why-can-the-timeline-move-backward-&quot;&gt;Why Can the Timeline Move Backward?&lt;/h4&gt;
&lt;p&gt;DSA View View is &lt;strong&gt;not reversing JavaScript while it runs&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Inside the Web Worker, it first rewrites the code into an instrumented version and records the execution.&lt;/p&gt;
&lt;p&gt;During playback, the main value that changes is &lt;code&gt;currentStep&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;type ExecutionState = {
  currentStep: number;
  totalSteps: number;
  steps: ExecutionStep[];
  isComplete: boolean;
  returnValue?: unknown;
  error?: string;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Moving forward displays the snapshot for &lt;code&gt;currentStep + 1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Moving backward displays the snapshot for &lt;code&gt;currentStep - 1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So rewinding means,&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;selecting an earlier recorded frame, not undoing the execution itself&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once recording has finished, users can navigate without rerunning their code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pause&lt;/li&gt;
&lt;li&gt;Move one step forward or backward&lt;/li&gt;
&lt;li&gt;Jump to any step in the timeline&lt;/li&gt;
&lt;li&gt;Move to the first or final step&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Users can also select a variable and jump directly to the previous step where its value changed.&lt;/p&gt;
&lt;p&gt;That is especially useful for pointers such as &lt;code&gt;i&lt;/code&gt;, &lt;code&gt;left&lt;/code&gt;, and &lt;code&gt;right&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;No more clicking the Back button 47 times while whispering, &quot;Where did you go wrong?&quot; 😸&lt;/p&gt;
&lt;h4 id=&quot;there-is-a-generator-but-it-does-not-suspend-the-user-s-code-line-by-line&quot;&gt;There Is a Generator, but It Does Not Suspend the User’s Code Line by Line&lt;/h4&gt;
&lt;p&gt;The Runtime API contains a &lt;code&gt;Generator&amp;lt;ExecutionStep, ...&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That might make it look as if the user’s code itself is being paused line by line with &lt;code&gt;yield&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It is not.&lt;/p&gt;
&lt;p&gt;After yielding the first &lt;code&gt;function-call&lt;/code&gt; step, the instrumented code runs once and accumulates steps inside the execution context.&lt;/p&gt;
&lt;p&gt;The generator then yields the already-recorded steps in order.&lt;/p&gt;
&lt;p&gt;In the path used by the UI, the Worker consumes the generator completely and returns the finished &lt;code&gt;ExecutionState&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Separating &lt;strong&gt;execution&lt;/strong&gt; from &lt;strong&gt;playback&lt;/strong&gt; is what makes the rewind behavior relatively simple.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-web-worker-do-not-take-the-ui-down-with-you&quot;&gt;⚙️ Web Worker: Do Not Take the UI Down with You&lt;/h3&gt;
&lt;p&gt;User code runs inside a dedicated Web Worker instead of the main thread.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;new Worker(new URL(&quot;./execution-worker.ts&quot;, import.meta.url), {
  type: &quot;module&quot;,
  name: &quot;algorithm-execution&quot;,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An infinite loop or expensive computation on the main thread would freeze the entire page.&lt;/p&gt;
&lt;p&gt;By moving execution into a Worker:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The React UI stays on the main thread&lt;/li&gt;
&lt;li&gt;Starting a new run or leaving the page can terminate the Worker through an &lt;code&gt;AbortController&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Runtime also has limits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Execution timeout: 10 seconds&lt;/li&gt;
&lt;li&gt;Maximum recorded steps: 3,000&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a limit is reached, the Worker can be terminated or execution can stop early with a warning.&lt;/p&gt;
&lt;h4 id=&quot;a-worker-is-not-a-security-sandbox&quot;&gt;A Worker Is Not a Security Sandbox&lt;/h4&gt;
&lt;p&gt;A Web Worker does not have access to the page DOM, &lt;code&gt;localStorage&lt;/code&gt;, or &lt;code&gt;sessionStorage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;DSA View View also does not execute user code on a server.&lt;/p&gt;
&lt;p&gt;However, &lt;strong&gt;a Web Worker is not a security boundary that makes untrusted code safe.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The user code runs through &lt;code&gt;new Function&lt;/code&gt; in the Worker global scope.&lt;/p&gt;
&lt;p&gt;Web APIs available inside Workers, including &lt;code&gt;fetch&lt;/code&gt;, are not automatically disabled.&lt;/p&gt;
&lt;p&gt;The purpose of this architecture is to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Isolate heavy execution from the main thread&lt;/li&gt;
&lt;li&gt;Terminate an execution unit on timeout or cancellation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is an execution boundary for responsiveness and control, not a magical prison for JavaScript.&lt;/p&gt;
&lt;p&gt;JavaScript has watched enough escape-room videos to know better.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-visualization-variables-alone-are-not-enough&quot;&gt;👀 Visualization: Variables Alone Are Not Enough&lt;/h3&gt;
&lt;p&gt;The Runtime returns an &lt;code&gt;ExecutionState&lt;/code&gt; and snapshots for each step.&lt;/p&gt;
&lt;p&gt;The next problem is deciding which View should display that trace.&lt;/p&gt;
&lt;p&gt;DSA View View does not ask AI,&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Hey, does this look like Binary Search?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;on every run.&lt;/p&gt;
&lt;p&gt;Instead, visualization candidates are detected through logic based on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Variable names&lt;/li&gt;
&lt;li&gt;Variable types and data structures&lt;/li&gt;
&lt;li&gt;Whether arrays or matrices actually changed&lt;/li&gt;
&lt;li&gt;Pointers such as &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, and &lt;code&gt;mid&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The call stack&lt;/li&gt;
&lt;li&gt;Function-entry and return history&lt;/li&gt;
&lt;li&gt;Heap metadata&lt;/li&gt;
&lt;li&gt;Changes across multiple steps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, displaying a Sort Graph whenever a &lt;code&gt;number[]&lt;/code&gt; exists would classify algorithms that only &lt;em&gt;read&lt;/em&gt; an input array as sorting algorithms.&lt;/p&gt;
&lt;p&gt;So the detection logic checks whether the numeric array actually changes between steps.&lt;/p&gt;
&lt;p&gt;It also looks for context such as &lt;code&gt;sort&lt;/code&gt;, &lt;code&gt;swap&lt;/code&gt;, &lt;code&gt;partition&lt;/code&gt;, and &lt;code&gt;pivot&lt;/code&gt; in descriptions or the call stack.&lt;/p&gt;
&lt;p&gt;Some representative mappings are 👇&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Runtime state                                 &lt;/th&gt;&lt;th&gt;Displayed View&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;A changing numeric array                      &lt;/td&gt;&lt;td&gt;Sort Graph&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;An array used as a stack                      &lt;/td&gt;&lt;td&gt;Stack View&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Search using &lt;code&gt;left&lt;/code&gt; / &lt;code&gt;right&lt;/code&gt; / &lt;code&gt;mid&lt;/code&gt;         &lt;/td&gt;&lt;td&gt;Index View&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Sliding Window pointers                       &lt;/td&gt;&lt;td&gt;Sliding Window View&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A two-dimensional array                       &lt;/td&gt;&lt;td&gt;Matrix View&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;DP arrays or rolling state                    &lt;/td&gt;&lt;td&gt;DP View&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;TreeNode&lt;/code&gt;                                    &lt;/td&gt;&lt;td&gt;Tree Graph&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;ListNode&lt;/code&gt;                                    &lt;/td&gt;&lt;td&gt;List Graph&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Recursive function entry and return           &lt;/td&gt;&lt;td&gt;Recursion Tree&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Logic using &lt;code&gt;Map&lt;/code&gt;                             &lt;/td&gt;&lt;td&gt;Map View&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Logic using both &lt;code&gt;MinHeap&lt;/code&gt; and &lt;code&gt;MaxHeap&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;Heap View&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Word Ladder-style BFS                         &lt;/td&gt;&lt;td&gt;Word Ladder View&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Container / rain-water / histogram pointers   &lt;/td&gt;&lt;td&gt;Area View&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Calculator character positions and sign stack &lt;/td&gt;&lt;td&gt;Expression View&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;When multiple candidate Views are found, DSA View View assigns priorities and selects a primary Visualization.&lt;/p&gt;
&lt;p&gt;For example, if heap state is available, Heap View takes priority over an ordinary array View.&lt;/p&gt;
&lt;h4 id=&quot;visualization-is-built-from-executionstate&quot;&gt;Visualization Is Built from &lt;code&gt;ExecutionState&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Each Visualizer knows nothing about the user’s source code.&lt;/p&gt;
&lt;p&gt;It extracts what it needs from the current &lt;code&gt;ExecutionState&lt;/code&gt; and converts that data into View-specific state.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;&amp;lt;BinarySearchVisualizer
  data={data}
  indexState={{
    left,
    right,
    mid,
  }}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When &lt;code&gt;currentStep&lt;/code&gt; changes, the same Visualizer receives a new state.&lt;/p&gt;
&lt;p&gt;CSS transitions and Framer Motion animate the difference, making pointers and nodes appear to move.&lt;/p&gt;
&lt;p&gt;This creates a clean separation of responsibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Runtime creates a correct trace while preserving the original behavior&lt;/li&gt;
&lt;li&gt;Detection decides which View best represents the trace&lt;/li&gt;
&lt;li&gt;Visualization focuses on how to display that state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adding a new visualization generally follows this path:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Detect the target execution state&lt;/li&gt;
&lt;li&gt;Convert it into View-specific state&lt;/li&gt;
&lt;li&gt;Render it with a React component&lt;/li&gt;
&lt;li&gt;Add it to the primary-visualization selection&lt;/li&gt;
&lt;li&gt;Add the smallest reproducible code sample as a test&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-tests-reducing-the-number-of-it-happened-to-work-cases&quot;&gt;🧪 Tests: Reducing the Number of &quot;It Happened to Work&quot; Cases&lt;/h3&gt;
&lt;p&gt;Users can write code freely, which means they can also write it in ways I never expected.&lt;/p&gt;
&lt;p&gt;AST transformations are especially dangerous because fixing one syntax pattern can quietly break another.&lt;/p&gt;
&lt;p&gt;So when a real issue appears, I keep the smallest version of that code as a regression test.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Arguments and local variables for each recursive call&lt;/li&gt;
&lt;li&gt;Postfix &lt;code&gt;i++&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;XOR assignment&lt;/li&gt;
&lt;li&gt;Nested array mutations&lt;/li&gt;
&lt;li&gt;Class constructors and &lt;code&gt;super()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TreeNode&lt;/code&gt; / &lt;code&gt;ListNode&lt;/code&gt; inputs&lt;/li&gt;
&lt;li&gt;Heap operations&lt;/li&gt;
&lt;li&gt;Binary Search and Sliding Window pointers&lt;/li&gt;
&lt;li&gt;Word Ladder BFS state&lt;/li&gt;
&lt;li&gt;Worker timeouts and step limits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Trusting human memory to say,&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Surely this syntax still works.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;is not a particularly strong testing strategy.&lt;/p&gt;
&lt;p&gt;Memory is surprisingly optimistic.&lt;/p&gt;
&lt;p&gt;And it would be a little embarrassing to build a tool for avoiding confusion in DSA, only to get lost inside the tool’s own behavior.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-summary&quot;&gt;🎯 Summary&lt;/h3&gt;
&lt;p&gt;DSA View View is not simply turning an array into a bar chart.&lt;/p&gt;
&lt;p&gt;At a high level, it works like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Write TypeScript in Monaco&lt;/li&gt;
&lt;li&gt;Analyze the source code and signature with Babel&lt;/li&gt;
&lt;li&gt;Inject &lt;code&gt;recordStep&lt;/code&gt; into the AST&lt;/li&gt;
&lt;li&gt;Execute the code inside a Web Worker&lt;/li&gt;
&lt;li&gt;Record variable snapshots and the call stack&lt;/li&gt;
&lt;li&gt;Replay the steps with React&lt;/li&gt;
&lt;li&gt;Display a View matching the data structure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The most important step was converting the user’s code into an array of &lt;code&gt;ExecutionStep&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;Once execution becomes a sequence of steps, the same &lt;code&gt;ExecutionState&lt;/code&gt; can power:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pause&lt;/li&gt;
&lt;li&gt;Forward and backward navigation&lt;/li&gt;
&lt;li&gt;Timeline jumps&lt;/li&gt;
&lt;li&gt;Navigation to the previous variable change&lt;/li&gt;
&lt;li&gt;Reconstruction of recursion trees&lt;/li&gt;
&lt;li&gt;Data-structure visualizations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key idea is &quot;Do not try to reverse JavaScript itself. First, transform execution into a recordable domain model&quot;.&lt;/p&gt;
&lt;p&gt;Then,&lt;/p&gt;
&lt;p&gt;&quot;Runtime focuses on producing a correct trace, while Visualization focuses on presenting that trace.&quot;&lt;/p&gt;
&lt;p&gt;Keeping Editor, Runtime, and Visualization independent from one another has been essential to making the tool extensible. Stable models such as &lt;code&gt;ExecutionStep&lt;/code&gt; and &lt;code&gt;ExecutionState&lt;/code&gt; connect these layers without coupling them directly.&lt;/p&gt;
&lt;p&gt;There are still unsupported algorithms and syntax patterns, so I would love to hear from you if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An implementation does not run&lt;/li&gt;
&lt;li&gt;You want another data structure visualized&lt;/li&gt;
&lt;li&gt;You think a trace would be clearer in a different View&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Issues and PRs are always welcome 👇&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now let’s run our code and watch what it is doing inside together. 👀👀&lt;/p&gt;
&lt;p&gt;And if you enjoy the project, I would be very happy if you left a GitHub ⭐!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23738/how-i-made-javascript-execution-visual-and-rewindable-dsa-view-view</guid>
<pubDate>Wed, 29 Jul 2026 11:39:13 +0000</pubDate>
</item>
<item>
<title>How I Built the Editor and Compile Pipeline for a TypeScript DSA Visualizer (DSA View View 👀👀)</title>
<link>https://coderlegion.com/23243/how-built-the-editor-and-compile-pipeline-for-a-typescript-dsa-visualizer-dsa-view-view</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I'm &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer. Thanks to my daily Duolingo streak, Dutch is finally starting to sound slightly less like one extremely long word. Progress! 🦉&lt;/p&gt;
&lt;p&gt;In my previous article, I introduced &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;strong&gt;DSA View View&lt;/strong&gt;&lt;/a&gt;, a tool for visualizing data structures and algorithm execution.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/i2nterqnwmb72g6w56ur.gif&quot; alt=&quot;DSA View View fish&quot;&gt;&lt;/p&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&quot;&gt;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;That article covers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why I decided to study DSA again&lt;/li&gt;
&lt;li&gt;What DSA View View is&lt;/li&gt;
&lt;li&gt;How to use it&lt;/li&gt;
&lt;li&gt;How I plan to study with it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So if this is your first time meeting &lt;strong&gt;Mr. View&lt;/strong&gt;, the previous article is a good place to start.&lt;/p&gt;
&lt;p&gt;This time, we're opening the hood.&lt;/p&gt;
&lt;p&gt;This is the first half of the implementation story, covering everything from the moment a user writes &lt;code&gt;TypeScript&lt;/code&gt; to the moment &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;DSA View View&lt;/a&gt; generates an input form for that implementation.&lt;/p&gt;
&lt;p&gt;We'll mainly look at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The overall architecture and FSD&lt;/li&gt;
&lt;li&gt;Runtime type guards with &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;&lt;code&gt;is-kit&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Monaco&lt;/code&gt; Editor&lt;/li&gt;
&lt;li&gt;Static &lt;code&gt;TypeScript&lt;/code&gt; analysis&lt;/li&gt;
&lt;li&gt;Compilation with &lt;code&gt;Babel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Extracting function signatures from the AST&lt;/li&gt;
&lt;li&gt;Generating the Verification form&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find the source code here:&lt;/p&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;All right, let's take a look together!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-designing-dsa-view-view&quot;&gt;📝 Designing DSA View View&lt;/h3&gt;
&lt;p&gt;DSA View View has four main responsibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Editor&lt;/li&gt;
&lt;li&gt;Compile&lt;/li&gt;
&lt;li&gt;Runtime&lt;/li&gt;
&lt;li&gt;Visualize&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is also a Verification step in the UI, where the user provides input values before the code is executed.&lt;/p&gt;
&lt;p&gt;The overall flow looks like below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ri5ak9q7gj5ugn0imxmq.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;Each part is already a little complicated on its own, and they interact with one another quite deeply.&lt;/p&gt;
&lt;p&gt;I could see during the design phase that a flat project structure would eventually leave future-me wandering around the repository like a lost hiker. 🌲&lt;/p&gt;
&lt;p&gt;So I decided to use &lt;strong&gt;FSD (Feature-Sliced Design)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I previously wrote about how I learned FSD here.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/lets-learn-feature-sliced-design-fsd-15bb&quot;&gt;https://dev.to/nyaomaru/lets-learn-feature-sliced-design-fsd-15bb&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A simplified version of the project structure looks like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;src/
├── app/                # App startup and global styles
├── pages/
│   └── main/           # Main page that composes the features
├── widgets/
│   ├── editor-panel/   # UI around the editor
│   ├── control-panel/  # Compile / Verification / Runtime controls
│   └── header/
├── features/
│   ├── code-editing/   # Monaco Editor, formatting, and parsing
│   ├── compilation/    # Compile behavior
│   ├── code-execution/ # AST transforms, execution, step recording, input forms
│   ├── visualization/  # Visualization detection and views
│   └── shareable-url/
├── entities/
│   ├── code/           # Code, types, and compile results
│   ├── execution/      # ExecutionState and ExecutionStep
│   ├── data-structure/ # TreeNode, ListNode, and friends
│   └── algorithm-example/
└── shared/
    ├── ui/             # Generic components such as Button, Card, and Dialog
    └── lib/            # Guards, cloning, serializers, and other utilities
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, &lt;code&gt;features/code-execution&lt;/code&gt; owns the behavior of executing code.&lt;/p&gt;
&lt;p&gt;On the other hand, &lt;code&gt;ExecutionState&lt;/code&gt; and &lt;code&gt;ExecutionStep&lt;/code&gt; describe domain concepts, so they live in &lt;code&gt;entities/execution&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then &lt;code&gt;pages/main&lt;/code&gt; combines the individual features and coordinates the overall flow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;After Compile succeeds, extract the signature and move to Verification&lt;/li&gt;
&lt;li&gt;After receiving the inputs, start the Runtime&lt;/li&gt;
&lt;li&gt;When the Runtime step changes, highlight the corresponding line in the Editor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each slice is consumed through a public API such as &lt;code&gt;index.ts&lt;/code&gt;, and dependencies are allowed to flow only from higher layers to lower layers.&lt;/p&gt;
&lt;p&gt;I also verify those dependency directions and public API boundaries with architecture tests.&lt;/p&gt;
&lt;p&gt;Drawing those boundaries helps prevent the classic.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;I changed the Runtime. Why is the Editor broken now?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A mystery nobody ordered.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-is-kit-protecting-the-app-from-unknown-hell&quot;&gt;🛡️ &lt;code&gt;is-kit&lt;/code&gt;: Protecting the App from &lt;code&gt;unknown&lt;/code&gt; Hell&lt;/h3&gt;
&lt;p&gt;FSD can organize dependency directions, but it cannot automatically make values entering the application safe.&lt;/p&gt;
&lt;p&gt;In &lt;strong&gt;DSA View View&lt;/strong&gt;, mysterious &lt;code&gt;unknown&lt;/code&gt; values come flying in from many directions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AST nodes returned by &lt;code&gt;Babel Parser&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Messages sent between &lt;code&gt;Web Worker&lt;/code&gt;s&lt;/li&gt;
&lt;li&gt;JSON restored from a shareable URL&lt;/li&gt;
&lt;li&gt;Runtime snapshots recorded from user code&lt;/li&gt;
&lt;li&gt;Arrays, trees, graphs, and heaps being inspected as visualization candidates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;TypeScript&lt;/code&gt; is wonderfully reliable at compile time.&lt;/p&gt;
&lt;p&gt;But at runtime, it does not interrogate a value and ask.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Are you &lt;em&gt;really&lt;/em&gt; an &lt;code&gt;ExecutionState&lt;/code&gt;, or are you just wearing its name tag?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is where my type guard library, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;&lt;code&gt;is-kit&lt;/code&gt;&lt;/a&gt;, comes in.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;&lt;code&gt;is-kit&lt;/code&gt;&lt;/a&gt; is a small, zero-dependency library for building and composing reusable &lt;code&gt;TypeScript&lt;/code&gt; type guards.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import {
  and,
  arrayOf,
  define,
  hasKey,
  isArray,
  isNumber,
  isObject,
  isString,
  not,
  oneOfValues,
  or,
  predicateToRefine,
  type Guard,
} from &quot;is-kit&quot;;

const isNumericString = and(
  isString,
  predicateToRefine&amp;lt;string&amp;gt;((value) =&amp;gt; {
    return value.trim() !== &quot;&quot; &amp;amp;&amp;amp; Number.isFinite(Number(value));
  }),
);

const isNonArrayObject = and(isObject, not(isArray));
const isNumericValue = or(isNumber, isNumericString);
const isNumericArray = arrayOf(isNumericValue);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Small guards can be combined like LEGO bricks to create more complicated runtime checks.&lt;/p&gt;
&lt;p&gt;For example, the code that finds class methods in an AST defines a guard.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const isMethodNodeType = oneOfValues(&quot;ClassMethod&quot;, &quot;MethodDefinition&quot;);

const hasType = hasKey(&quot;type&quot;);

const isMethodLikeNode: Guard&amp;lt;MethodLikeNode&amp;gt; = and(
  isObject,
  define&amp;lt;MethodLikeNode&amp;gt;(
    (value) =&amp;gt; hasType(value) &amp;amp;&amp;amp; isMethodNodeType(value.type),
  ),
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a value passes this guard, it is narrowed from a plain &lt;code&gt;unknown&lt;/code&gt; to &lt;code&gt;MethodLikeNode&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The guard can then be passed directly to &lt;code&gt;filter&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const members = classNode.body.body.filter(isMethodLikeNode);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is extremely convenient.&lt;/p&gt;
&lt;p&gt;Without &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;&lt;code&gt;is-kit&lt;/code&gt;&lt;/a&gt;, checks like this would have multiplied across the repository:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;typeof value === &quot;object&quot; &amp;amp;&amp;amp;
value !== null &amp;amp;&amp;amp;
&quot;type&quot; in value &amp;amp;&amp;amp;
Array.isArray(...)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eventually, slightly different versions of the same guard would appear everywhere.&lt;/p&gt;
&lt;p&gt;Six months later, I would be asking:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;What is the difference between this &lt;code&gt;isObject&lt;/code&gt; and that &lt;code&gt;isObject&lt;/code&gt;?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And congratulations—we would have built a second maze inside the first maze.&lt;/p&gt;
&lt;p&gt;At the time of writing, shared guards are used from around &lt;strong&gt;69 files&lt;/strong&gt; under &lt;code&gt;src&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;Monaco&lt;/code&gt; and &lt;code&gt;Babel&lt;/code&gt; are the stars on stage, &lt;code&gt;is-kit&lt;/code&gt; is the very serious bouncer standing at every entrance to the AST, Worker, share URL, Runtime, and Visualization layers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mr. Unknown&lt;/strong&gt; is not getting in.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/vyiop20o5q45i7tix443.png&quot; alt=&quot;Mr. Unknown insect damage&quot;&gt;&lt;/p&gt;
&lt;p&gt;I also use &lt;code&gt;Zod&lt;/code&gt; in the Verification forms, but the responsibilities are different:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;is-kit&lt;/code&gt;: Internal narrowing, filtering, and reusable runtime guards&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Zod&lt;/code&gt;: Input form schemas, validation messages, and user-facing errors&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of making one library solve every problem, &lt;code&gt;is-kit&lt;/code&gt; handles internal runtime safety while &lt;code&gt;Zod&lt;/code&gt; handles user input validation.&lt;/p&gt;
&lt;p&gt;I am very glad I built &lt;code&gt;is-kit&lt;/code&gt; before building this application. Fighting all those &lt;code&gt;unknown&lt;/code&gt; values with my bare hands would have made &lt;em&gt;me&lt;/em&gt; the first thing that needed visualizing.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-editor&quot;&gt;✏️ Editor&lt;/h3&gt;
&lt;p&gt;The Editor uses &lt;a rel=&quot;nofollow&quot; href=&quot;https://microsoft.github.io/monaco-editor/&quot;&gt;Monaco Editor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Monaco is the browser-focused editor component also used by &lt;code&gt;VS Code&lt;/code&gt;, giving the application syntax highlighting, completion, and &lt;code&gt;TypeScript&lt;/code&gt; diagnostics.&lt;/p&gt;
&lt;p&gt;The downside is that Monaco is not exactly tiny.&lt;/p&gt;
&lt;p&gt;Loading everything immediately made the initial display heavier, so DSA View View first renders a lightweight &lt;code&gt;textarea&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The same &lt;code&gt;textarea&lt;/code&gt; is also used as the &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; fallback, preventing the editor area from becoming empty while Monaco is loading.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;&amp;lt;textarea
  aria-label=&quot;Code Editor&quot;
  value={value}
  onChange={(event) =&amp;gt; onChange(event.target.value)}
  onFocus={onActivate}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Behind the scenes, DSA View View waits briefly and prepares Monaco with &lt;code&gt;requestIdleCallback&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the browser does not support &lt;code&gt;requestIdleCallback&lt;/code&gt;, loading starts normally. If the user focuses the &lt;code&gt;textarea&lt;/code&gt; before that, Monaco preparation begins immediately.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const CodeEditor = lazy(() =&amp;gt;
  prepareCodeEditorModule().then((module) =&amp;gt; ({
    default: module.CodeEditor,
  })),
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once loading finishes, the lightweight editor is replaced by Monaco while preserving the same source code.&lt;/p&gt;
&lt;p&gt;It looks like an editor, but secretly it is still a &lt;code&gt;textarea&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A tiny editor body-double strategy.&lt;/p&gt;
&lt;h4 id=&quot;static-typescript-analysis&quot;&gt;Static &lt;code&gt;TypeScript&lt;/code&gt; Analysis&lt;/h4&gt;
&lt;p&gt;Static analysis uses the &lt;code&gt;TypeScript Worker&lt;/code&gt; included with Monaco.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;TypeScript Language Service&lt;/code&gt; runs in a separate Worker from the editor itself and returns type and syntax problems as markers.&lt;/p&gt;
&lt;p&gt;Those markers are converted into the application's &lt;code&gt;CompilationError&lt;/code&gt; model and used to control the Compile button and display errors.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;type CompilationError = {
  line: number;
  column: number;
  message: string;
  severity: &quot;error&quot; | &quot;warning&quot;;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Compile button is disabled only when an &lt;code&gt;error&lt;/code&gt; exists. Warnings alone do not prevent compilation.&lt;/p&gt;
&lt;p&gt;One important detail is that &lt;strong&gt;&lt;code&gt;Babel&lt;/code&gt; does not perform &lt;code&gt;TypeScript&lt;/code&gt; type checking&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The responsibilities are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Monaco's &lt;code&gt;TypeScript&lt;/code&gt; Worker: Diagnose types and syntax&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Babel&lt;/code&gt;: Transform &lt;code&gt;TypeScript&lt;/code&gt; syntax into &lt;code&gt;JavaScript&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keeping that distinction clear saved me from blaming Babel for crimes it did not commit.&lt;/p&gt;
&lt;h4 id=&quot;registering-dsa-classes-in-the-editor&quot;&gt;Registering DSA Classes in the Editor&lt;/h4&gt;
&lt;p&gt;Many &lt;code&gt;LeetCode&lt;/code&gt; problems provide classes such as &lt;code&gt;TreeNode&lt;/code&gt; and &lt;code&gt;ListNode&lt;/code&gt; automatically.&lt;/p&gt;
&lt;p&gt;DSA View View also provides common classes so users do not have to declare them every time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TreeNode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ListNode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GraphNode&lt;/code&gt; / &lt;code&gt;_Node&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TrieNode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PriorityQueue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MinHeap&lt;/code&gt; / &lt;code&gt;MaxHeap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Deque&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UnionFind&lt;/code&gt; / &lt;code&gt;DSU&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Counter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Monaco provides a mechanism called &lt;code&gt;setExtraLibs&lt;/code&gt;, which lets the application add these type definitions to the Editor:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;monaco.typescript.typescriptDefaults.setExtraLibs([
  {
    content,
    filePath: &quot;ts:algorithm-visualizer-prepared-classes.ts&quot;,
  },
]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, &lt;code&gt;setExtraLibs&lt;/code&gt; only supports type analysis inside the Editor. It does not magically create those classes at Runtime.&lt;/p&gt;
&lt;p&gt;So the Compile and Runtime pipelines also prepend the same class definitions to the user's code as a prelude.&lt;/p&gt;
&lt;p&gt;If the user declares a &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;interface&lt;/code&gt;, or &lt;code&gt;type&lt;/code&gt; with the same name, DSA View View skips injecting the prepared definition.&lt;/p&gt;
&lt;p&gt;Imagine carefully writing your own &lt;code&gt;ListNode&lt;/code&gt;, only for another &lt;code&gt;ListNode&lt;/code&gt; to emerge from behind it.&lt;/p&gt;
&lt;p&gt;That is not type safety. That is a summer horror movie.&lt;/p&gt;
&lt;h4 id=&quot;formatting&quot;&gt;Formatting&lt;/h4&gt;
&lt;p&gt;Formatting uses &lt;code&gt;Prettier&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Prettier core, &lt;code&gt;TypeScript&lt;/code&gt; plugin, and &lt;code&gt;Estree&lt;/code&gt; plugin are dynamically imported only when needed and registered as Monaco's &lt;code&gt;Document Formatting Provider&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Users can therefore format their code with &lt;code&gt;Command/Ctrl + S&lt;/code&gt; or &lt;code&gt;Shift + Alt + F&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-from-the-compile-button-to-verification&quot;&gt;📌 From the Compile Button to Verification&lt;/h3&gt;
&lt;p&gt;So far, I have described &quot;Compile&quot; as if it were one large operation.&lt;/p&gt;
&lt;p&gt;Internally, however, the FSD slice &lt;code&gt;features/compilation&lt;/code&gt; does not own the whole flow.&lt;/p&gt;
&lt;p&gt;When the user presses the Compile button, &lt;code&gt;pages/main&lt;/code&gt; coordinates these operations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;features/compilation&lt;/code&gt; checks whether the &lt;code&gt;TypeScript&lt;/code&gt; can be transformed into &lt;code&gt;JavaScript&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The parser in &lt;code&gt;features/code-editing&lt;/code&gt; extracts the function or class signature&lt;/li&gt;
&lt;li&gt;If everything succeeds, the UI moves to Verification&lt;/li&gt;
&lt;li&gt;&lt;code&gt;features/code-execution&lt;/code&gt; displays an input form matching that signature&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To the user, this feels like one continuous Compile step.&lt;/p&gt;
&lt;p&gt;Inside the application, each responsibility remains separate.&lt;/p&gt;
&lt;h4 id=&quot;transforming-typescript-with-babel&quot;&gt;Transforming &lt;code&gt;TypeScript&lt;/code&gt; with Babel&lt;/h4&gt;
&lt;p&gt;The application uses Babel to transform &lt;code&gt;TypeScript&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const prepared = prepareTypeScriptCodeWithClasses(code);

const result = transform(prepared.code, {
  presets: [&quot;typescript&quot;],
  filename: &quot;input.ts&quot;,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DSA View View runs entirely in the browser, so it is not invoking &lt;code&gt;tsc&lt;/code&gt; on a &lt;code&gt;Node.js&lt;/code&gt; server.&lt;/p&gt;
&lt;p&gt;Instead, it uses &lt;code&gt;@babel/standalone&lt;/code&gt; to transform &lt;code&gt;TypeScript&lt;/code&gt; syntax into &lt;code&gt;JavaScript&lt;/code&gt; in the browser.&lt;/p&gt;
&lt;p&gt;Before transformation, the application prepends any prepared classes that the user has not declared themselves.&lt;/p&gt;
&lt;p&gt;If the source contains imports for those prepared classes, they are removed because the browser execution environment cannot resolve those modules.&lt;/p&gt;
&lt;p&gt;Again, an important reminder:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Babel's &lt;code&gt;TypeScript&lt;/code&gt; preset primarily removes type information. It does not type-check the program.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Monaco handles type errors. Babel checks whether the syntax can be transformed.&lt;/p&gt;
&lt;p&gt;The generated &lt;code&gt;JavaScript&lt;/code&gt; from this stage is also not executed directly on the main thread.&lt;/p&gt;
&lt;p&gt;For the Runtime, the original source is instrumented separately and then executed inside a &lt;code&gt;Web Worker&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That is the fun part—and the topic of the next article.&lt;/p&gt;
&lt;h4 id=&quot;extracting-function-signatures-from-the-ast&quot;&gt;Extracting Function Signatures from the AST&lt;/h4&gt;
&lt;p&gt;After Compile succeeds, DSA View View needs to build an input form.&lt;/p&gt;
&lt;p&gt;Suppose the user writes this function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function trap(height: number[]): number {
  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The source code is parsed into an AST with &lt;code&gt;Babel Parser&lt;/code&gt;, and DSA View View extracts information like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;{
  name: &quot;trap&quot;,
  parameters: [
    {
      name: &quot;height&quot;,
      type: &quot;number-array&quot;,
      optional: false,
    },
  ],
  returnType: &quot;number&quot;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The parser supports more than ordinary function declarations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Function declarations&lt;/li&gt;
&lt;li&gt;Arrow functions assigned to variables&lt;/li&gt;
&lt;li&gt;Function expressions assigned to variables&lt;/li&gt;
&lt;li&gt;LeetCode-style factory functions that return another function&lt;/li&gt;
&lt;li&gt;Class declarations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a class, the parser extracts the constructor and callable methods. &lt;code&gt;private&lt;/code&gt; methods are currently excluded.&lt;/p&gt;
&lt;p&gt;For class-design problems such as &lt;code&gt;LRUCache&lt;/code&gt; and &lt;code&gt;MedianFinder&lt;/code&gt;, it extracts the class name, constructor, method names, and arguments.&lt;/p&gt;
&lt;p&gt;When the source contains multiple helper classes, DSA View View avoids prioritizing prepared classes as solution candidates and compares the number of callable methods to select the likely design class.&lt;/p&gt;
&lt;p&gt;This selection is heuristic, so it cannot perfectly identify every possible class structure yet.&lt;/p&gt;
&lt;p&gt;There are always more creative ways to write JavaScript than I expect. JavaScript considers this a feature.&lt;/p&gt;
&lt;h4 id=&quot;generating-the-verification-form&quot;&gt;Generating the Verification Form&lt;/h4&gt;
&lt;p&gt;For ordinary functions, the input form is built from the extracted signature using &lt;code&gt;React Hook Form&lt;/code&gt; and &lt;code&gt;Zod&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Supported input types include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;number&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;number[]&lt;/code&gt; / &lt;code&gt;string[]&lt;/code&gt; / &lt;code&gt;boolean[]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;number[][]&lt;/code&gt; / &lt;code&gt;string[][]&lt;/code&gt; / &lt;code&gt;boolean[][]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TreeNode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ListNode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GraphNode&lt;/code&gt; / &lt;code&gt;_Node&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The input UI and validation behavior change depending on the type.&lt;/p&gt;
&lt;p&gt;For example, a &lt;code&gt;number[]&lt;/code&gt; accepts both:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1, 2, 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both formats are converted into a real array that can be passed to the Runtime.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;TreeNode&lt;/code&gt;, the application constructs a tree from a level-order array.&lt;/p&gt;
&lt;p&gt;When a problem has multiple &lt;code&gt;TreeNode&lt;/code&gt; parameters, users can also select a specific node from the first root.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;ListNode&lt;/code&gt;, users can build a linked list from an array or node-based input and specify the cycle position.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;GraphNode&lt;/code&gt;, a cyclic graph structure is constructed from a &lt;code&gt;LeetCode&lt;/code&gt;-style adjacency list.&lt;/p&gt;
&lt;p&gt;Class-design problems use a different UI.&lt;/p&gt;
&lt;p&gt;Instead of the normal parameter form powered by &lt;code&gt;React Hook Form&lt;/code&gt; and &lt;code&gt;Zod&lt;/code&gt;, DSA View View displays the same &lt;code&gt;Operations&lt;/code&gt; and &lt;code&gt;Arguments&lt;/code&gt; JSON format used by &lt;code&gt;LeetCode&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;MedianFinder&lt;/code&gt; uses:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Operations: [&quot;MedianFinder&quot;, &quot;addNum&quot;, &quot;addNum&quot;, &quot;findMedian&quot;]
Arguments:  [[], [1], [2], []]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This form validates class-design-specific requirements, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The number of operations matches the number of argument lists&lt;/li&gt;
&lt;li&gt;The first operation matches the class name&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the Compile button starts a chain of separate responsibilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Babel verifies that transformation is possible&lt;/li&gt;
&lt;li&gt;AST analysis extracts the signature&lt;/li&gt;
&lt;li&gt;The Verification layer generates the appropriate input form&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pages/main&lt;/code&gt; connects the entire flow from the Editor to Verification&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-summary-1&quot;&gt;🎯 Summary&lt;/h3&gt;
&lt;p&gt;In this article, we followed the path from writing &lt;code&gt;TypeScript&lt;/code&gt; in DSA View View to generating the inputs needed for execution.&lt;/p&gt;
&lt;p&gt;The broad flow is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Render a lightweight &lt;code&gt;textarea&lt;/code&gt; first&lt;/li&gt;
&lt;li&gt;Prepare Monaco Editor in the background&lt;/li&gt;
&lt;li&gt;Diagnose types and syntax with the &lt;code&gt;TypeScript&lt;/code&gt; Worker&lt;/li&gt;
&lt;li&gt;Use Babel to check whether the &lt;code&gt;TypeScript&lt;/code&gt; can be transformed into &lt;code&gt;JavaScript&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Extract the function or class signature from the AST&lt;/li&gt;
&lt;li&gt;Generate a Verification form based on the detected types&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The important design decision was not forcing the Editor, diagnostics, syntax transformation, and input UI into one giant operation.&lt;/p&gt;
&lt;p&gt;Instead:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;&lt;code&gt;is-kit&lt;/code&gt;&lt;/a&gt; safely narrows &lt;code&gt;unknown&lt;/code&gt; values from the AST, Workers, JSON, and runtime snapshots&lt;/li&gt;
&lt;li&gt;Monaco's &lt;code&gt;TypeScript&lt;/code&gt; Worker diagnoses types and syntax&lt;/li&gt;
&lt;li&gt;Babel transforms &lt;code&gt;TypeScript&lt;/code&gt; syntax into &lt;code&gt;JavaScript&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;AST analysis extracts the signature needed by the Verification UI&lt;/li&gt;
&lt;li&gt;The Verification form validates inputs and converts them into Runtime values&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pages/main&lt;/code&gt; connects the separate steps and advances the UI&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keeping those responsibilities separate made the flow much easier to understand and extend.&lt;/p&gt;
&lt;p&gt;In the next article, we will finally enter the heart of DSA View View: the Runtime.&lt;/p&gt;
&lt;p&gt;That is where &lt;strong&gt;Mr. View&lt;/strong&gt; stops politely waiting beside the Editor and starts watching every move your variables make. 👀&lt;/p&gt;
&lt;p&gt;There are still unsupported patterns and types, so Issues and PRs are always welcome!&lt;/p&gt;
&lt;p&gt;And if you enjoy the project, I would be very happy if you left a GitHub ⭐!&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23243/how-built-the-editor-and-compile-pipeline-for-a-typescript-dsa-visualizer-dsa-view-view</guid>
<pubDate>Wed, 22 Jul 2026 14:19:08 +0000</pubDate>
</item>
<item>
<title>I Built a Tool to Visualize DSA. Let’s Learn Together! (DSA View View 👀👀)</title>
<link>https://coderlegion.com/22758/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer currently fighting the intense European heatwave by building DIY window screens here in the Netherlands. 🛠️&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/data-science-collective/should-you-still-learn-to-code-in-2026-034685e17707&quot;&gt;https://medium.com/data-science-collective/should-you-still-learn-to-code-in-2026-034685e17707&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As this article also discusses, I believe that &lt;strong&gt;improving our fundamental engineering skills is still essential in the age of AI&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Imagine that AI makes an F1 car fully autonomous. 🏎️&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Would you let someone without even a driver’s license enter a race as its driver?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Or imagine that AI completely automates maintenance during pit stops. &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Would you put someone who knows absolutely nothing about cars, their parts, or their internal structure in charge of that AI?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;AI is certainly reducing the amount of code we need to write by hand. Typing quickly and memorizing every small piece of syntax or every method are becoming less important.&lt;/p&gt;
&lt;p&gt;But &lt;strong&gt;can we confidently release a product without understanding its fundamental concepts or implementation?&lt;/strong&gt; If something goes wrong in production, can we take responsibility and fix it?&lt;/p&gt;
&lt;p&gt;My answer was &lt;strong&gt;no&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Building things faster is obviously a good thing. But if we build without understanding, we may simply produce fragile software with no long-term sustainability at a faster rate.&lt;/p&gt;
&lt;p&gt;The real value comes from building meaningful things quickly.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Then, how can we develop the fundamental skills we still need in the AI era?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I believe one useful approach is learning &lt;strong&gt;DSA: Data Structures and Algorithms&lt;/strong&gt;. So I decided to start learning DSA again.&lt;/p&gt;
&lt;p&gt;However, while solving problems on &lt;code&gt;LeetCode&lt;/code&gt;, I repeatedly ran into the same problem.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I want to visualize the data flow and understand what is happening, but I still can’t clearly see it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So I used the skills I have developed through frontend engineering and built a tool that visualizes the execution flow of DSA implementations! 🚀&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/7z2mrykqy49xd42oxyod.gif&quot; alt=&quot;DSA View View demo&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this article, I’ll explain how &lt;strong&gt;DSA View View&lt;/strong&gt; works and how we can use it to study DSA.&lt;/p&gt;
&lt;p&gt;Let’s take a look together!&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;br&gt;
I may sound confident so far, but I’m definitely not a DSA expert. I’m still learning, just like many of you.&lt;/p&gt;
&lt;p&gt;In fact, I built this tool precisely because I was struggling. When I realized that I couldn’t immediately solve Binary Search, I started to panic a little. 😸&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-is-dsa-view-view-&quot;&gt;🖥️ What Is DSA View View?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;DSA View View&lt;/strong&gt; is a tool that executes functions and classes written in &lt;code&gt;TypeScript&lt;/code&gt; and lets you inspect how variables and return values change on each line through a runtime timeline.&lt;/p&gt;
&lt;p&gt;The easiest way to understand is to try it yourself!&lt;/p&gt;
&lt;p&gt;Open the tool and click the &lt;strong&gt;Run Demo&lt;/strong&gt; button.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/hd0vewv2n72vf15buviw.png&quot; alt=&quot;Run Demo Button&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You’ll quickly see how the workflow works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Write your implementation&lt;/li&gt;
&lt;li&gt;Compile it&lt;/li&gt;
&lt;li&gt;Enter the input values&lt;/li&gt;
&lt;li&gt;Run it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s really it.&lt;/p&gt;
&lt;p&gt;Although I created it primarily for DSA problems, you can also use it to inspect and test smaller &lt;code&gt;TypeScript&lt;/code&gt; implementations.&lt;/p&gt;
&lt;p&gt;And, of course, it’s completely free!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-can-it-do-&quot;&gt;🤔 What Can It Do?&lt;/h3&gt;
&lt;p&gt;With DSA View View, you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write DSA implementations in &lt;code&gt;TypeScript&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Test implementations with different input values&lt;/li&gt;
&lt;li&gt;Move forward and backward through runtime steps&lt;/li&gt;
&lt;li&gt;Visualize supported data structures and algorithms&lt;/li&gt;
&lt;li&gt;Review LeetCode solutions and confirm your understanding&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also use it as a lightweight environment for executing functions or formatting code blocks for technical articles such as Dev.to 😸&lt;/p&gt;
&lt;p&gt;Feel free to experiment with it and find your own use cases!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-how-to-use-it&quot;&gt;🧑🏫 How to Use It&lt;/h3&gt;
&lt;p&gt;The main workflow consists of three modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Editor&lt;/li&gt;
&lt;li&gt;Verification&lt;/li&gt;
&lt;li&gt;Runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s look at each one.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;editor&quot;&gt;Editor&lt;/h3&gt;
&lt;p&gt;First, write your implementation!&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/xfn7q92x88jgncmku5ia.png&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Editor is designed to feel similar to a basic IDE.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TypeScript&lt;/code&gt; diagnostics are available by default, so the editor can detect some errors while you write.&lt;/p&gt;
&lt;p&gt;You can also press &lt;code&gt;Cmd/Ctrl + S&lt;/code&gt; to format your code with Prettier, so feel free to write first and clean it up afterward.&lt;/p&gt;
&lt;p&gt;Common classes used in DSA problems, such as &lt;code&gt;TreeNode&lt;/code&gt; and &lt;code&gt;ListNode&lt;/code&gt;, are already available.&lt;/p&gt;
&lt;p&gt;You don’t need to declare or import them every time.&lt;/p&gt;
&lt;p&gt;You can also define your own classes or replace the provided definitions when necessary.&lt;/p&gt;
&lt;p&gt;Once your implementation is ready, click &lt;strong&gt;Compile Code&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If compilation fails, the tool displays the relevant location and error message so that you can fix it.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/1np9j681z3850xkqk2zv.png&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is also an &lt;strong&gt;Example&lt;/strong&gt; menu containing several common implementations.&lt;/p&gt;
&lt;p&gt;For example, if you want to quickly inspect a Heap Sort implementation and see how it behaves, you can load the example instead of writing everything manually.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;verification&quot;&gt;Verification&lt;/h3&gt;
&lt;p&gt;In Verification mode, you define the input values that should be passed to your function or class.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ya388vbn1ihnn98qadwv.png&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Enter a test value for each argument.&lt;/p&gt;
&lt;p&gt;For arrays, both of the following formats are supported:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1, 2, 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once your input values are ready, click &lt;strong&gt;Run&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;runtime&quot;&gt;Runtime&lt;/h3&gt;
&lt;p&gt;Finally, let’s inspect the runtime!&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/4qbwieq7xxhytjfni4pe.png&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When you click &lt;strong&gt;Run&lt;/strong&gt; in Verification mode, your implementation is executed.&lt;/p&gt;
&lt;p&gt;Runtime mode then lets you play through the execution steps or move backward and forward manually.&lt;/p&gt;
&lt;p&gt;When the tool recognizes supported logic, &lt;strong&gt;an additional visualization is displayed in a modal&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Not every possible implementation is supported yet, of course. If something you need is missing, I would be happy to receive an issue or pull request!&lt;/p&gt;
&lt;p&gt;You can move backward and forward through the executed lines to inspect:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which line is currently running&lt;/li&gt;
&lt;li&gt;How variables change&lt;/li&gt;
&lt;li&gt;Where values are returned&lt;/li&gt;
&lt;li&gt;How the execution reaches its final result&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can return to the beginning, jump directly to the end, or move through the timeline one step at a time.&lt;/p&gt;
&lt;p&gt;The main execution process runs entirely in the browser.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;share-&quot;&gt;Share 🐈&lt;/h3&gt;
&lt;p&gt;DSA View View also includes a sharing feature.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/0nw9ys2wj0mbh41yty50.png&quot; alt=&quot;DSA View View Share button&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can create a URL that preserves your implementation and share it with someone else.&lt;/p&gt;
&lt;p&gt;Use the &lt;strong&gt;Share&lt;/strong&gt; button at the top of the page to post it on social media or copy the link directly.&lt;/p&gt;
&lt;p&gt;Share your implementations, compare different solutions, and let’s learn together!&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The shared URL contains your implementation. Do not paste private, confidential, or proprietary code into the tool before sharing it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;what-is-that-thing-in-the-top-right-corner-&quot;&gt;What Is That Thing in the Top-Right Corner? 👀👀&lt;/h3&gt;
&lt;p&gt;Meet Mr. View.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/adl9obeidbaf1fn0ufv6.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mr. View watches over your implementation.&lt;/p&gt;
&lt;p&gt;Nothing more.&lt;/p&gt;
&lt;p&gt;Nothing less.&lt;/p&gt;
&lt;p&gt;He is simply there to support you.&lt;/p&gt;
&lt;p&gt;When you need to concentrate, press the TV button to hide him. Press it again to bring him back.&lt;/p&gt;
&lt;p&gt;However, if Mr. View is enough to distract you, you may not be ready for an attack from ten simultaneous Slack notifications. 😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-how-should-we-study-dsa-&quot;&gt;✏️ How Should We Study DSA?&lt;/h3&gt;
&lt;p&gt;Now you know how to use DSA View View.&lt;/p&gt;
&lt;p&gt;The next question is probably&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Which problems should I solve?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;LeetCode is an obvious place to start, and the study plan from Tech Interview Handbook also provides a useful collection of practice problems. 👇&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.techinterviewhandbook.org/coding-interview-study-plan/&quot;&gt;https://www.techinterviewhandbook.org/coding-interview-study-plan/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Try solving these problems with &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;DSA View View&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once your implementation works, use the runtime timeline to inspect the data flow and understand exactly how the algorithm reaches its answer.&lt;/p&gt;
&lt;p&gt;You can also use tools such as ChatGPT, Codex, Claude, Claude Code, Gemini, and Grok to explore your solution further.&lt;/p&gt;
&lt;p&gt;Don’t limit yourself to asking&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Is this implementation correct?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Try asking questions such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What would happen if I rewrote this DFS solution using BFS?&lt;/li&gt;
&lt;li&gt;Could Dynamic Programming solve this problem?&lt;/li&gt;
&lt;li&gt;Are there any other reasonable approaches?&lt;/li&gt;
&lt;li&gt;What are the time and space complexities?&lt;/li&gt;
&lt;li&gt;Why is this approach better than the alternatives?&lt;/li&gt;
&lt;li&gt;Under which conditions would another approach be better?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI becomes much more useful for learning when it helps us compare and understand different approaches instead of only generating a final answer. 👍&lt;/p&gt;
&lt;h4 id=&quot;why-typescript-instead-of-python-&quot;&gt;Why TypeScript Instead of Python? 🐍&lt;/h4&gt;
&lt;p&gt;The simple answer is that &lt;code&gt;TypeScript&lt;/code&gt; is my favorite language.&lt;/p&gt;
&lt;p&gt;I mainly work as a frontend engineer, so &lt;code&gt;TypeScript&lt;/code&gt; is also the language in which I feel most comfortable experimenting and learning.&lt;/p&gt;
&lt;p&gt;However, I designed the tool so that additional languages could be supported in the future.&lt;/p&gt;
&lt;p&gt;If there is enough demand for another language, please leave a comment or open an issue!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion-4&quot;&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;AI is helping us write code faster and faster.&lt;/p&gt;
&lt;p&gt;However, understanding what happens inside an implementation remains an important engineering skill.&lt;/p&gt;
&lt;p&gt;In fact, I believe that this ability may become even more valuable as AI-generated code becomes increasingly common.&lt;/p&gt;
&lt;p&gt;I built DSA View View because I wanted a tool that could help me strengthen those fundamental skills from the ground up.&lt;/p&gt;
&lt;p&gt;To be honest, I still have a lot to learn about DSA.&lt;/p&gt;
&lt;p&gt;That is exactly why I hope we can learn together.&lt;/p&gt;
&lt;p&gt;Please leave a comment or open an issue if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want support for a particular problem or data structure&lt;/li&gt;
&lt;li&gt;You have an idea for a new visualization&lt;/li&gt;
&lt;li&gt;You find a bug&lt;/li&gt;
&lt;li&gt;You want support for another programming language&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s train our DSA muscles together! 💪😸&lt;/p&gt;
&lt;p&gt;If you like it, please give a star ⭐&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-vie&quot;&gt;https://github.com/nyaomaru/dsa-view-vie&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And I also launched on ProductHunt! Come say hello and let me know what you think 😸 👇&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.producthunt.com/products/dsa-view-view&quot;&gt;https://www.producthunt.com/products/dsa-view-view&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22758/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view</guid>
<pubDate>Wed, 15 Jul 2026 13:00:13 +0000</pubDate>
</item>
<item>
<title>BFS vs DFS? The Question That Fixed My Graph Solutions</title>
<link>https://coderlegion.com/18704/bfs-vs-dfs-the-question-that-fixed-my-graph-solutions</link>
<description>&lt;p&gt;For a long time I treated BFS and DFS as interchangeable. Two ways to visit every node, pick whichever came to mind first. Then an interview handed me a grid that wanted the minimum number of steps, and a recursive DFS felt faster to type, so that's what went down. The path that came back was valid and far too long. No error. Just an answer that passed the small cases and fell apart on the one that mattered, with no obvious reason why.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What finally clicked:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The two traversals hold different invariants. They don't just visit in a different order.&lt;/li&gt;
&lt;li&gt;The queue gives BFS its shortest path guarantee. The stack makes DFS commit to one branch.&lt;/li&gt;
&lt;li&gt;&quot;Minimum&quot; in an unweighted graph turns out to be a reliable signal for BFS.&lt;/li&gt;
&lt;li&gt;&quot;Detect&quot;, &quot;order&quot;, and &quot;all paths&quot; point to DFS.&lt;/li&gt;
&lt;li&gt;Once edges carry weights, neither applies, and Dijkstra's algorithm takes over.&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;The mistake wasn't picking the slower traversal. It was reaching for whichever traversal got practiced most recently instead of reading what the problem actually asked for.&lt;/p&gt;
&lt;p&gt;A grid that wants the fewest steps is asking for a distance guarantee, and DFS doesn't offer one. It happily commits to a branch and walks 30 cells deep before it would ever consider the 8 cell route, then stops the moment it touches the goal, satisfied with a path that happens to be valid. The wrong choice there has nothing to do with DFS being worse. It comes from never asking what kind of answer the problem needed.&lt;/p&gt;
&lt;h3 id=&quot;the-one-question-that-changed-it&quot;&gt;The one question that changed it&lt;/h3&gt;
&lt;p&gt;Before writing any code now, the question is always the same: does this problem need a distance guarantee or a structural property? If the answer is distance, that's BFS. If it's structure, meaning cycles, ordering, or which nodes connect to which, that's DFS.&lt;/p&gt;
&lt;p&gt;It sounds almost too simple, and that's the point. The decision was never about which algorithm you can implement faster. It's about which guarantee the problem is quietly demanding.&lt;/p&gt;
&lt;h3 id=&quot;what-that-looked-like-on-the-same-six-node-graph&quot;&gt;What that looked like on the same six node graph&lt;/h3&gt;
&lt;p&gt;The clearest way to feel the difference is to run both on one small graph and watch the order. Take six nodes with edges 0→1, 0→2, 1→3, 1→4, 2→4, 2→5, starting from node 0.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from collections import deque

graph = {0: [1, 2], 1: [3, 4], 2: [4, 5], 3: [], 4: [], 5: []}

def bfs(start):
    order, seen, q = [], {start}, deque([start])
    while q:
        node = q.popleft()
        order.append(node)
        for nxt in graph[node]:
            if nxt not in seen:
                seen.add(nxt)
                q.append(nxt)
    return order

def dfs(start):
    order, seen, stack = [], set(), [start]
    while stack:
        node = stack.pop()
        if node in seen:
            continue
        seen.add(node)
        order.append(node)
        for nxt in reversed(graph[node]):
            if nxt not in seen:
                stack.append(nxt)
    return order

print(bfs(0))  # [0, 1, 2, 3, 4, 5]
print(dfs(0))  # [0, 1, 3, 4, 2, 5]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;BFS visits in exact distance order: everything one hop from the source (1, 2) before anything two hops away (3, 4, 5). DFS visits 3 before it ever gets back to 2, even though 3 sits further from the source. It cares about depth, not distance. Swapping the queue for a stack is the only structural change between the two functions, and it produces the entire difference in behaviour.&lt;/p&gt;
&lt;h3 id=&quot;why-this-works-the-part-that-took-digging-&quot;&gt;Why this works (the part that took digging)&lt;/h3&gt;
&lt;p&gt;A queue is first in, first out, so the nodes you discover at distance &lt;code&gt;d&lt;/code&gt; always come out behind the remaining &lt;code&gt;d-1&lt;/code&gt; nodes and ahead of the &lt;code&gt;d+1&lt;/code&gt; ones. That ordering is the shortest path guarantee, with no explicit distance tracking needed. A stack is last in, first out, so the most recent branch always gets expanded next, which is depth first by construction.&lt;/p&gt;
&lt;p&gt;There's a memory split that follows from the same fact. BFS holds the full frontier, so its peak memory tracks the widest level of the graph. DFS holds only the current path, so its memory tracks the deepest one. Tracing both algorithms frame by frame in &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/courses/graph&quot;&gt;the graph course&lt;/a&gt;, watching the queue and stack contents change at every step, is what makes the distance ordering stop feeling abstract.&lt;/p&gt;
&lt;h3 id=&quot;what-to-do-when-you-re-still-guessing&quot;&gt;What to do when you're still guessing&lt;/h3&gt;
&lt;p&gt;If you're staring at a graph problem and reaching for habit, run it through this instead:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;If the problem asks for &lt;/th&gt;&lt;th&gt;Reach for&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Shortest path or fewest moves, equal cost edges &lt;/td&gt;&lt;td&gt;BFS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Level by level processing &lt;/td&gt;&lt;td&gt;BFS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Nearest target &lt;/td&gt;&lt;td&gt;BFS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Cycle detection &lt;/td&gt;&lt;td&gt;DFS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Topological order &lt;/td&gt;&lt;td&gt;DFS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Connected components &lt;/td&gt;&lt;td&gt;DFS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;All paths or backtracking &lt;/td&gt;&lt;td&gt;DFS&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shortest path with weighted edges &lt;/td&gt;&lt;td&gt;Neither, use Dijkstra&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I originally posted this on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/blogs/bfs-vs-dfs&quot;&gt;my own blog&lt;/a&gt;, a slightly longer version with a directed cycle detection walkthrough.&lt;/p&gt;
&lt;p&gt;What's a graph problem that finally made the BFS versus DFS choice click for you, the one where guessing wrong actually cost you in an interview?&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18704/bfs-vs-dfs-the-question-that-fixed-my-graph-solutions</guid>
<pubDate>Fri, 22 May 2026 20:01:47 +0000</pubDate>
</item>
<item>
<title>FAANG Coding Interviews Aren’t the Same: The DSA Patterns Each Company Actually Tests</title>
<link>https://coderlegion.com/17592/faang-coding-interviews-arent-the-same-the-dsa-patterns-each-company-actually-tests</link>
<description>&lt;p&gt;&lt;em&gt;After treating every FAANG company like the same interview, Amazon exposed the gap in about twenty minutes.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I'd built FAANG prep around a small set of pattern families that looked highest-frequency: predicate search, sliding window, hash table fundamentals, and a few graph traversals.&lt;/p&gt;
&lt;p&gt;The mental model was that these were the patterns &quot;every big tech round&quot; sampled from.&lt;/p&gt;
&lt;p&gt;The first Amazon round asked about Randomised Set design followed by a Queue Design follow up.&lt;/p&gt;
&lt;p&gt;Neither was in the deep set the plan covered.&lt;/p&gt;
&lt;p&gt;I solved the first one with effort and improvised the second one badly.&lt;/p&gt;
&lt;p&gt;The interview went south in the last 20 minutes.&lt;/p&gt;
&lt;p&gt;The thing missing wasn't a specific pattern.&lt;/p&gt;
&lt;p&gt;It was that Amazon's pattern footprint is meaningfully wider than Google's, and the prep allocation behind every hour was treating every FAANG company as if they sampled from the same bag.&lt;/p&gt;
&lt;p&gt;Here's what came out of mapping company tags across 450+ handpicked FAANG problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Seven patterns are universal across 6+ major companies. Skipping any of them is a structural hole.&lt;/li&gt;
&lt;li&gt;Amazon spans 11+ pattern families, the broadest in the data. &quot;Study three patterns deeply&quot; doesn't work there.&lt;/li&gt;
&lt;li&gt;Google has narrower coverage but a distinctive emphasis on &lt;code&gt;predicate search&lt;/code&gt; (binary search on the answer space).&lt;/li&gt;
&lt;li&gt;Meta wants hash table depth and design fluency in a tighter pattern band.&lt;/li&gt;
&lt;li&gt;Prefix Sum is the single most undertrained pattern relative to its 8-company tag count.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;where-the-old-approach-was-breaking&quot;&gt;Where the old approach was breaking&lt;/h3&gt;
&lt;p&gt;The old plan looked roughly like this:&lt;/p&gt;
&lt;p&gt;Pick five or six pattern families that look universal-ish, drill them to the point you can write the canonical solution from memory, then trust that volume on adjacent problems will generalise to whatever the interview throws.&lt;/p&gt;
&lt;p&gt;The implicit bet was that every FAANG company is a sample from roughly the same distribution, and the mean of the drilled patterns covers the mean of the question pool.&lt;/p&gt;
&lt;p&gt;The data didn't support the bet.&lt;/p&gt;
&lt;p&gt;The surprising part wasn't that companies differed.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;It was how much they differed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Across the company tags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google's distribution skewed toward predicate search and graph reasoning. Narrower set, harder reasoning per problem.&lt;/li&gt;
&lt;li&gt;Amazon's distribution was the flattest of any company in the set. Eleven plus pattern families, none dominant.&lt;/li&gt;
&lt;li&gt;Meta's distribution clustered tight: sliding window, prefix sum, counting, design.&lt;/li&gt;
&lt;li&gt;Microsoft had a quietly distinct cluster: 2D binary search and staircase search, multi-dimensional patterns most plans skip.&lt;/li&gt;
&lt;li&gt;Apple's distribution was the inverse of Amazon's: deep on fundamentals (especially counting), almost no advanced design.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The mistake wasn't drilling the wrong patterns.&lt;/p&gt;
&lt;p&gt;It was applying the same allocation across companies that didn't share the same allocation.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-shift-that-worked&quot;&gt;The shift that worked&lt;/h3&gt;
&lt;p&gt;The shift was small in description and large in practice.&lt;/p&gt;
&lt;p&gt;Instead of one prep plan, the move was to build a &lt;strong&gt;per-company allocation&lt;/strong&gt;, anchored to the universal patterns underneath.&lt;/p&gt;
&lt;p&gt;Concretely:&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;Drill 5–6 patterns deeply, hope coverage generalises      &lt;/td&gt;&lt;td&gt;Cover 7 universals first, then add 2–4 patterns specific to the target company&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Pick problems by data structure (Hash Table, Tree, Graph) &lt;/td&gt;&lt;td&gt;Pick problems by pattern, then filter by company tag&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Treat all FAANG rounds as the same problem distribution   &lt;/td&gt;&lt;td&gt;Allocate by the company’s actual tag profile&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Practise at uniform intensity across pattern families     &lt;/td&gt;&lt;td&gt;Front load the universals, then go deep on company-specific gaps&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The 7-pattern universal layer never moved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LRU Cache&lt;/li&gt;
&lt;li&gt;Counting&lt;/li&gt;
&lt;li&gt;Backtracking&lt;/li&gt;
&lt;li&gt;Prefix Sum&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Fixed Sliding Window&lt;/li&gt;
&lt;li&gt;Variable Sliding Window&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What changed was what came after.&lt;/p&gt;
&lt;p&gt;For an Amazon round, the next four patterns to drill were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Randomised Set&lt;/li&gt;
&lt;li&gt;Queue Design&lt;/li&gt;
&lt;li&gt;2D Binary Search&lt;/li&gt;
&lt;li&gt;Staircase Search&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;plus dedicated time on the Bar Raiser follow up format.&lt;/p&gt;
&lt;p&gt;For Google, the next two patterns were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Predicate Search&lt;/li&gt;
&lt;li&gt;Graph reasoning&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;drilled deeper.&lt;/p&gt;
&lt;p&gt;For Meta:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Design depth (&lt;code&gt;LRU Cache&lt;/code&gt; from scratch, &lt;code&gt;Randomised Set&lt;/code&gt; from scratch)&lt;/li&gt;
&lt;li&gt;Maximum Predicate Search&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The total prep time stayed roughly the same.&lt;/p&gt;
&lt;p&gt;The allocation was different.&lt;/p&gt;
&lt;hr&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;The clearest example was &lt;code&gt;LRU Cache&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The canonical solution had been in the toolkit for years:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;hash map + doubly linked list&lt;/li&gt;
&lt;li&gt;&lt;code&gt;O(1)&lt;/code&gt; per operation&lt;/li&gt;
&lt;li&gt;&lt;code&gt;head&lt;/code&gt; and &lt;code&gt;tail&lt;/code&gt; sentinels&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What I'd never done was rebuild it under a constraint shift.&lt;/p&gt;
&lt;p&gt;Which is exactly what the Amazon Bar Raiser round likes to test.&lt;/p&gt;
&lt;p&gt;The setup is the standard one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.map = {}
        self.head = ListNode()                    # most recently used end
        self.tail = ListNode()                    # least recently used end
        self.head.next = self.tail
        self.tail.prev = self.head

    def get(self, key):
        if key not in self.map:
            return -1
        node = self.map[key]
        self._remove(node)
        self._add_to_front(node)
        return node.value

    def put(self, key, value):
        if key in self.map:
            self._remove(self.map[key])

        node = ListNode(key, value)
        self._add_to_front(node)
        self.map[key] = node

        if len(self.map) &amp;gt; self.capacity:
            lru = self.tail.prev
            self._remove(lru)
            del self.map[lru.key]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The constraint shift Amazon likes to ask:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Now each entry has a TTL. A read on an expired entry returns &lt;code&gt;-1&lt;/code&gt;. The cache still operates at expected &lt;code&gt;O(1)&lt;/code&gt; per call.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That's the moment the canonical answer stops being enough.&lt;/p&gt;
&lt;p&gt;The candidate who memorised LRU starts redesigning from scratch.&lt;/p&gt;
&lt;p&gt;The candidate who built the original from the invariant asks one question first:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Which invariant did the doubly linked list maintain, and does that invariant still hold under TTL?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The list maintained an ordering:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Nodes near &lt;code&gt;head&lt;/code&gt; were used more recently than nodes near &lt;code&gt;tail&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The eviction criterion changes from:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Least recently used”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Expired or least recently used.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The list ordering by recency is still useful for the second clause.&lt;/p&gt;
&lt;p&gt;The first clause needs a different signal.&lt;/p&gt;
&lt;p&gt;That's where &lt;strong&gt;lazy expiry&lt;/strong&gt; (timestamp checked on read) and &lt;strong&gt;eager expiry&lt;/strong&gt; (min heap on TTL) diverge.&lt;/p&gt;
&lt;p&gt;The canonical LRU sits in most candidates' heads.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The constraint flip doesn't.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once practising the follow up explicitly becomes part of the loop, on every design problem in the universal set, the conversion rate on Amazon-style rounds shifts noticeably.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-pattern-that-s-underrated-for-years&quot;&gt;The pattern that's underrated for years&lt;/h3&gt;
&lt;p&gt;Looking back at the tag data, the single biggest miscalibration in the old plan was &lt;strong&gt;Prefix Sum&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Eight companies tag it.&lt;/p&gt;
&lt;p&gt;That's the same as Backtracking.&lt;/p&gt;
&lt;p&gt;More than almost every classic searching variant except the basic one.&lt;/p&gt;
&lt;p&gt;Most prep plans treat it like a minor utility pattern because it doesn't have a flagship problem the way &lt;code&gt;LRU Cache&lt;/code&gt; does.&lt;/p&gt;
&lt;p&gt;In the data, it punches above its weight:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Subarray Sum Equals K&lt;/li&gt;
&lt;li&gt;Equilibrium Index&lt;/li&gt;
&lt;li&gt;Count Subarrays Divisible by K&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;especially in Meta and Amazon variants.&lt;/p&gt;
&lt;p&gt;The fix took less than a weekend.&lt;/p&gt;
&lt;p&gt;Prefix Sum's mechanic is small enough that two evenings of focused drilling cover it.&lt;/p&gt;
&lt;p&gt;The composite version (&lt;strong&gt;prefix sum + hash map of seen sums&lt;/strong&gt;) is the FAANG-shaped one, and it's the one most prep plans bury under a miscellaneous heading.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-works-for-someone-in-the-same-spot&quot;&gt;What works for someone in the same spot&lt;/h3&gt;
&lt;p&gt;Three rules from the retrospective:&lt;/p&gt;
&lt;h4 id=&quot;1-cover-the-seven-universals-first&quot;&gt;1. Cover the seven universals first&lt;/h4&gt;
&lt;p&gt;No exceptions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LRU Cache&lt;/li&gt;
&lt;li&gt;Counting&lt;/li&gt;
&lt;li&gt;Backtracking&lt;/li&gt;
&lt;li&gt;Prefix Sum&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Fixed Sliding Window&lt;/li&gt;
&lt;li&gt;Variable Sliding Window&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No matter the target company.&lt;/p&gt;
&lt;h4 id=&quot;2-specialise-second&quot;&gt;2. Specialise second&lt;/h4&gt;
&lt;p&gt;But specialise to the company’s actual tag profile.&lt;/p&gt;
&lt;p&gt;For Amazon:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;breadth&lt;/li&gt;
&lt;li&gt;constraint-flip follow ups&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For Google:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;predicate search&lt;/li&gt;
&lt;li&gt;graph reasoning depth&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For Meta:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;design depth&lt;/li&gt;
&lt;li&gt;sliding window fluency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For Microsoft:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2D + staircase search&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For Apple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fundamentals depth&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;3-cut-what-doesn-t-match&quot;&gt;3. Cut what doesn't match&lt;/h4&gt;
&lt;p&gt;Targeting Google and not Amazon?&lt;/p&gt;
&lt;p&gt;Queue Design can wait.&lt;/p&gt;
&lt;p&gt;Targeting Meta and not Microsoft?&lt;/p&gt;
&lt;p&gt;Staircase Search can wait.&lt;/p&gt;
&lt;p&gt;Prep time is finite.&lt;/p&gt;
&lt;p&gt;The data tells you where the false-priority work lives.&lt;/p&gt;
&lt;p&gt;The biggest shift, though, wasn't any one pattern.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“FAANG prep” isn't one allocation problem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;It's five separate ones with a shared seven-pattern foundation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The company determines which 2–4 patterns sit on top of it.&lt;/p&gt;
&lt;p&gt;Every hour spent assuming the foundation was all that mattered was an hour that could've gone to the layer above.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Originally posted in a longer form on my &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/blogs/amazon-vs-google-coding-interview-dsa&quot;&gt;own blog&lt;/a&gt;, with the per-company FAQ and a fuller breakdown of what each company quietly deemphasises.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you've interviewed at more than one FAANG, what was a pattern that came up where the company’s reputation hadn't prepared you for it?&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17592/faang-coding-interviews-arent-the-same-the-dsa-patterns-each-company-actually-tests</guid>
<pubDate>Tue, 12 May 2026 15:44:03 +0000</pubDate>
</item>
<item>
<title>Arrays vs Linked Lists: The Real Difference Most Interview Prep Misses</title>
<link>https://coderlegion.com/17281/arrays-vs-linked-lists-the-real-difference-most-interview-prep-misses</link>
<description>&lt;p&gt;&lt;em&gt;It took an &lt;code&gt;LRU Cache&lt;/code&gt; interview for me to realise the complexity table wasn't answering the real question.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I'd been picking between arrays and linked lists for years before realising the table wasn't the answer to the question.&lt;/p&gt;
&lt;p&gt;Then &lt;code&gt;LRU Cache&lt;/code&gt; came up in an interview, and the table didn't help: both structures were on the page, neither alone solved the problem, and the silence ran nine minutes before the interviewer gave a hint.&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;Arrays and linked lists differ for one reason: &lt;strong&gt;contiguous memory vs scattered memory&lt;/strong&gt;. Every entry on the complexity table follows from that single fact.&lt;/li&gt;
&lt;li&gt;Two &lt;code&gt;O(n)&lt;/code&gt; operations aren't equal in practice. An array scan of a million integers runs roughly an order of magnitude faster than the equivalent linked list traversal because of CPU cache locality.&lt;/li&gt;
&lt;li&gt;The &quot;&lt;code&gt;O(1)&lt;/code&gt; insertion&quot; claim about linked lists comes with a hidden prerequisite: the algorithm has to already hold a pointer to the position. Find it first, and you've paid &lt;code&gt;O(n)&lt;/code&gt; before the insertion runs.&lt;/li&gt;
&lt;li&gt;Many interesting interview problems combine both structures rather than picking one. &lt;code&gt;LRU Cache&lt;/code&gt; is the canonical case.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;where-the-table-stopped-working&quot;&gt;Where the table stopped working&lt;/h3&gt;
&lt;p&gt;For a long time the table &lt;em&gt;was&lt;/em&gt; the decision tree.&lt;/p&gt;
&lt;p&gt;The problem mentioned &quot;insert&quot; or &quot;delete,&quot; reach for a linked list. The problem mentioned &quot;access by index,&quot; reach for an array.&lt;/p&gt;
&lt;p&gt;That worked on simple problems.&lt;/p&gt;
&lt;p&gt;It fell apart on anything that needed both behaviours.&lt;/p&gt;
&lt;p&gt;The specific moment was sitting in front of &lt;code&gt;LRU Cache&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The problem wants &lt;code&gt;get(key)&lt;/code&gt; and &lt;code&gt;put(key, value)&lt;/code&gt; both in &lt;code&gt;O(1)&lt;/code&gt; average time, with eviction of the least recently used entry when the cache is full.&lt;/p&gt;
&lt;p&gt;A hash map handles the lookup half, since &lt;code&gt;get&lt;/code&gt; has to be &lt;code&gt;O(1)&lt;/code&gt; and a hash map is the obvious answer.&lt;/p&gt;
&lt;p&gt;Then comes the recency tracking, which means some kind of ordering on the entries.&lt;/p&gt;
&lt;p&gt;An array of &lt;code&gt;(key, timestamp)&lt;/code&gt; pairs would mean updating timestamps on access, and the eviction step turns into a linear scan for the smallest timestamp.&lt;/p&gt;
&lt;p&gt;That breaks the &lt;code&gt;O(1)&lt;/code&gt; requirement.&lt;/p&gt;
&lt;p&gt;Switch to a linked list and inserting at the front on every access is &lt;code&gt;O(1)&lt;/code&gt; &lt;em&gt;given a pointer to the node&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To find the node from a key, the algorithm walks the list, which is &lt;code&gt;O(n)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The hash map gives &lt;code&gt;O(1)&lt;/code&gt; lookups but no reordering.&lt;/p&gt;
&lt;p&gt;The linked list gives &lt;code&gt;O(1)&lt;/code&gt; reordering but no fast lookup.&lt;/p&gt;
&lt;p&gt;The problem wasn't picking the wrong structure.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The problem was thinking the question had only two answers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;the-realisation-that-broke-the-table&quot;&gt;The realisation that broke the table&lt;/h3&gt;
&lt;p&gt;The fix sounds obvious in retrospect:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keep both.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A hash map mapping keys to &lt;em&gt;linked list nodes&lt;/em&gt;, and a doubly linked list whose nodes store the values and recency order.&lt;/p&gt;
&lt;p&gt;The hash map gives &lt;code&gt;O(1)&lt;/code&gt; access to the exact node.&lt;/p&gt;
&lt;p&gt;Once the algorithm has the node, the linked list reorders without traversing in &lt;code&gt;O(1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Eviction becomes removing the tail node, which is also &lt;code&gt;O(1)&lt;/code&gt; because a tail pointer is maintained.&lt;/p&gt;
&lt;p&gt;The two structures aren't competing.&lt;/p&gt;
&lt;p&gt;They're solving different sub-problems.&lt;/p&gt;
&lt;p&gt;The hash map handles constant-time access.&lt;/p&gt;
&lt;p&gt;The linked list handles pointer reassignment at known positions.&lt;/p&gt;
&lt;p&gt;The qualifier on linked list insertion:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;&lt;code&gt;O(1)&lt;/code&gt; only if you already hold the pointer&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;finally has a structure feeding it those pointers for free.&lt;/p&gt;
&lt;p&gt;That's the same property powering a handful of linked list patterns that previously read like tricks: reversal, splitting, merging, fast and slow pointers.&lt;/p&gt;
&lt;p&gt;That's the realisation that broke the table.&lt;/p&gt;
&lt;p&gt;The table had been answering:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“If you pick exactly one structure, which one wins?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The interesting problems were asking a different question:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Which combination of memory layouts gives every operation in the algorithm the access pattern it needs?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;what-it-looks-like-in-code&quot;&gt;What it looks like in code&lt;/h3&gt;
&lt;p&gt;Here's the construction the technique walkthrough lands on:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Node:
    def __init__(self, key, value):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, capacity):
        self.capacity = capacity
        self.lookup = {}                  # hash map: key -&amp;gt; Node
        self.head = Node(0, 0)           # sentinel
        self.tail = Node(0, 0)           # sentinel
        self.head.next = self.tail
        self.tail.prev = self.head

    def _remove(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev

    def _add_front(self, node):
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node

    def get(self, key):
        if key not in self.lookup:
            return -1

        node = self.lookup[key]          # O(1) via hash map
        self._remove(node)               # O(1) via direct pointer
        self._add_front(node)            # O(1) splice at head

        return node.value

    def put(self, key, value):
        if key in self.lookup:
            self._remove(self.lookup[key])

        node = Node(key, value)
        self.lookup[key] = node
        self._add_front(node)

        if len(self.lookup) &amp;gt; self.capacity:
            lru = self.tail.prev         # O(1) via tail sentinel
            self._remove(lru)
            del self.lookup[lru.key]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every operation is &lt;code&gt;O(1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The hash map carries the lookup load.&lt;/p&gt;
&lt;p&gt;The doubly linked list carries the ordering load.&lt;/p&gt;
&lt;p&gt;Each structure does the thing it's actually good at, and the combination handles a problem neither could solve alone.&lt;/p&gt;
&lt;h3 id=&quot;what-i-d-tell-my-earlier-self&quot;&gt;What I'd tell my earlier self&lt;/h3&gt;
&lt;p&gt;The contrast between the old picking heuristic and the current one comes out cleanest side by side:&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;Match keywords in the prompt (&quot;insert&quot; → linked list) to the table. &lt;/td&gt;&lt;td&gt;Identify every operation the algorithm performs and check whether each one already has the pointer or index it needs.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Treat arrays and linked lists as competing answers.                 &lt;/td&gt;&lt;td&gt;Treat them as primitives that combine.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Quote &lt;code&gt;O(1)&lt;/code&gt; insertion for linked lists without the qualifier.      &lt;/td&gt;&lt;td&gt;Read &quot;&lt;code&gt;O(1)&lt;/code&gt; insertion&quot; as &quot;&lt;code&gt;O(1)&lt;/code&gt; once you've paid for the pointer.&quot;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Trust two &lt;code&gt;O(n)&lt;/code&gt; operations as equivalent in practice.              &lt;/td&gt;&lt;td&gt;Account for cache locality. Array scans often beat linked list traversals in real hardware.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And three rules that would've shortened the loop:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Identify the dominant operation first.&lt;/strong&gt; Before picking a structure, write down the access pattern the algorithm needs. &quot;Lookup by key, then mutate at that position&quot; is a different question than &quot;lookup by key, then iterate from there.&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check the qualifier on every linked list insertion claim.&lt;/strong&gt; Does the algorithm already hold the pointer, or does it have to walk to find it? If it has to walk, the advantage is gone.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When two structures each solve half the problem, try combining them.&lt;/strong&gt; That's the move that handles &lt;code&gt;LRU Cache&lt;/code&gt;, caching problems, and most eviction-style systems.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I wrote a &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/blogs/arrays-vs-linked-lists-difference&quot;&gt;longer version&lt;/a&gt; on Codeintuition that walks the same memory-layout reasoning through stacks, queues, and graph adjacency representations.&lt;/p&gt;
&lt;p&gt;If you want a deeper walkthrough of pointer-based mutation patterns, the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/learning-paths/data-structures/singly-linked-list&quot;&gt;singly linked list course&lt;/a&gt; on codeintuition covers seven of them with worked examples.&lt;/p&gt;
&lt;p&gt;What was the moment a data structure choice finally clicked for you, where the textbook framing had been hiding the actual question?&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17281/arrays-vs-linked-lists-the-real-difference-most-interview-prep-misses</guid>
<pubDate>Sun, 10 May 2026 13:06:23 +0000</pubDate>
</item>
<item>
<title>How to Know If You’re Ready for FAANG Interviews</title>
<link>https://coderlegion.com/17163/how-to-know-if-youre-ready-for-faang-interviews</link>
<description>&lt;p&gt;&lt;em&gt;After 280 LeetCode problems, I realised “feeling ready” wasn’t a metric. A diagnostic was.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I'd solved around 280 problems by the time the FAANG readiness question got serious, and the answer changed every week. Some weeks felt close after a clean batch of trees. Most weeks the next unfamiliar medium would freeze cold and the confidence would evaporate by Tuesday. The pattern repeated for months without resolving.&lt;/p&gt;
&lt;p&gt;It took longer than it should have to notice the question itself was the issue.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Do I feel ready&quot; wasn't a question with a stable answer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The threshold was real. The measurement of it was a mood read on a moving session.&lt;/p&gt;
&lt;h3 id=&quot;what-i-figured-out-1&quot;&gt;What I figured out&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Self assessed readiness drifts every session. A clean morning of trees and a frozen afternoon of graphs both produced wrong answers.&lt;/li&gt;
&lt;li&gt;The interview wasn't testing the same skill that practice on familiar problems trains. It was testing what holds up on shapes you haven't seen.&lt;/li&gt;
&lt;li&gt;Problem count flatlined as a useful signal somewhere around 100 problems. Past that, more volume on familiar shapes added count without moving the threshold.&lt;/li&gt;
&lt;li&gt;The fix was a measurable test, not more prep. An unseen medium, a hidden title, a real timer, repeated across three different families.&lt;/li&gt;
&lt;li&gt;The diagnostic was more useful than the score. &quot;Two passed, one failed on graphs&quot; was an action plan. &quot;Feeling 70 percent ready&quot; was anxiety.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Quick disclosure before I get into this: I built Codeintuition, a structured learning platform for coding interviews. This post is the retrospective on what someone should have handed me earlier, not a pitch for the product.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;where-the-approach-was-breaking&quot;&gt;Where the approach was breaking&lt;/h3&gt;
&lt;p&gt;The first thing that broke was the belief that the count was the metric.&lt;/p&gt;
&lt;p&gt;The tracker said 280, on its way to 350. The tracker also didn't predict any individual interview performance. Open a medium you haven't seen, freeze for ten minutes, and the count number sits there grinning at you, useless.&lt;/p&gt;
&lt;p&gt;The second thing that broke was the topic completion checkmark.&lt;/p&gt;
&lt;p&gt;Sliding window finished in February. By June the recall on a sliding window medium you hadn't seen was unreliable. The completion checkmark didn't decay.&lt;/p&gt;
&lt;p&gt;Recall did.&lt;/p&gt;
&lt;p&gt;The third thing that broke was speed on familiar problems.&lt;/p&gt;
&lt;p&gt;Two Sum in two minutes feels like fluency. It's actually retrieval of a stored solution. The first time you hit a problem that &lt;em&gt;looks&lt;/em&gt; like Two Sum but has a different constraint structure, the speed disappears and you're back to staring.&lt;/p&gt;
&lt;p&gt;The fourth thing was peer comparison, which broke quietly because it had never worked in the first place.&lt;/p&gt;
&lt;p&gt;Someone else got into Google in six months. That said nothing about your readiness. Background, family coverage, how they'd practised, the level they interviewed for, none of it generalises.&lt;/p&gt;
&lt;p&gt;What was actually happening is that all four signals were measuring the wrong thing.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The interview gives you a problem you haven't seen. The four signals all measured behaviour on problems you had seen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They were diagnostically useless for the question they were being asked to answer.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-diagnostic-that-ended-up-working&quot;&gt;The diagnostic that ended up working&lt;/h3&gt;
&lt;p&gt;The version that worked was small and strict.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Two hours. Three problems. Three different families. Real interview conditions.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The shape:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pick a pattern family genuinely studied (sliding window, tree traversal, graph BFS, DP subsequence)&lt;/li&gt;
&lt;li&gt;Find a medium difficulty problem in that family that has &lt;strong&gt;never&lt;/strong&gt; been solved, hinted, or read about&lt;/li&gt;
&lt;li&gt;Cover the problem name so the title can't reveal the family&lt;/li&gt;
&lt;li&gt;Twenty minute timer, hard cap on failed code execution attempts, no peeking&lt;/li&gt;
&lt;li&gt;Identify the family from the constraints, build the solution from the family's invariant, trace it before running&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A pass was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;solved within the timer&lt;/li&gt;
&lt;li&gt;fewer than two failed runs&lt;/li&gt;
&lt;li&gt;family identified from the constraints&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Anything else was data.&lt;/p&gt;
&lt;p&gt;Three passes across three families was a strong readiness signal. Recognition was firing on shapes you hadn't seen, the construction was following the family's invariant rather than recall, and the conditions were close enough to a real round that the result generalised.&lt;/p&gt;
&lt;p&gt;Two passes and one failure told you precisely which family to work on. No need for more random volume.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Depth in one specific place.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;All three failed was the most useful diagnostic of the three.&lt;/p&gt;
&lt;p&gt;It said the prep method had been building near transfer without far transfer, which is the gap described in the idea of Transfer of Learning.&lt;/p&gt;
&lt;p&gt;Method gap, not talent gap.&lt;/p&gt;
&lt;p&gt;The fix was correspondingly narrower than:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Grind more problems.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-that-looked-like-on-the-third-pass&quot;&gt;What that looked like on the third pass&lt;/h3&gt;
&lt;p&gt;The pass that taught the most was a DP subsequence problem run cold.&lt;/p&gt;
&lt;p&gt;The family had been studied. The canonical examples had been worked through. It hadn't been ground the way sliding window or trees had been ground, so it was a fair test of recognition rather than recall.&lt;/p&gt;
&lt;p&gt;The constraints described a longest increasing subsequence variant.&lt;/p&gt;
&lt;p&gt;With the title hidden, the recognition still happened in the first three minutes because the constraints fit the DP subsequence shape:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A one dimensional input, not two.&lt;/li&gt;
&lt;li&gt;An output asking for the longest sub structure satisfying a condition.&lt;/li&gt;
&lt;li&gt;The absence of two pointer or sliding window triggers that would have applied if the answer had been contiguous.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The skeleton came from the family's invariant, not from a remembered problem:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;dp[i]&lt;/code&gt; is the length of the longest valid subsequence ending at index &lt;code&gt;i&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def length_of_lis(nums):
    if not nums:
        return 0

    dp = [1] * len(nums)

    for i in range(1, len(nums)):
        for j in range(i):
            if nums[j] &amp;lt; nums[i]:
                dp[i] = max(dp[i], dp[j] + 1)

    return max(dp)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Trace on a five element array by hand. Verify the recurrence on the trace. Then run.&lt;/p&gt;
&lt;p&gt;It passed.&lt;/p&gt;
&lt;p&gt;Five minutes still on the timer.&lt;/p&gt;
&lt;p&gt;The check that mattered wasn't getting it right.&lt;/p&gt;
&lt;p&gt;It was how:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Family identified from the constraints inside three minutes&lt;/li&gt;
&lt;li&gt;Skeleton written from the invariant rather than retrieval&lt;/li&gt;
&lt;li&gt;Verified by tracing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's what pattern recognition holding under pressure looks like in practice.&lt;/p&gt;
&lt;p&gt;If any of those three steps had broken, the pass would have been a memory pass, not a recognition pass, and the family wouldn't actually have been ready yet.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-protocol-the-earlier-version-of-me-needed&quot;&gt;The protocol the earlier version of me needed&lt;/h3&gt;
&lt;p&gt;The change that mattered most was switching what practice was for.&lt;/p&gt;
&lt;p&gt;Up to about month four practice was for a problem set. After running the diagnostic for the first time, practice changed shape to feed the diagnostic.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Before the diagnostic                &lt;/th&gt;&lt;th&gt;After&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Solve next problem on the list       &lt;/td&gt;&lt;td&gt;Pick a family, work through three problems with the title hidden&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Read editorial when stuck            &lt;/td&gt;&lt;td&gt;Read the foundational material on the family's invariant when stuck&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Track problem count                  &lt;/td&gt;&lt;td&gt;Track families where the diagnostic passes cleanly&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Do problems in order of difficulty   &lt;/td&gt;&lt;td&gt;Do problems in order of how shallow recognition was on the family&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Speed up by recognising problems     &lt;/td&gt;&lt;td&gt;Speed up by recognising the family from constraints with the title hidden&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&quot;How many problems before I'm ready&quot; &lt;/td&gt;&lt;td&gt;&quot;How many families pass the diagnostic before I'm ready&quot;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Three rules ended up doing most of the work in the after column.&lt;/p&gt;
&lt;h4 id=&quot;1-one-unseen-medium-per-family-per-week&quot;&gt;1. One unseen medium per family per week&lt;/h4&gt;
&lt;p&gt;Title hidden.&lt;/p&gt;
&lt;p&gt;Timer running.&lt;/p&gt;
&lt;p&gt;Not as a substitute for the rest of practice.&lt;/p&gt;
&lt;p&gt;As the diagnostic that anchored what to practise next.&lt;/p&gt;
&lt;h4 id=&quot;2-foundational-material-on-the-family-s-invariant-before-more-volume&quot;&gt;2. Foundational material on the family's invariant before more volume&lt;/h4&gt;
&lt;p&gt;When a family failed the diagnostic, the next thing to read wasn't a new problem.&lt;/p&gt;
&lt;p&gt;It was the page on what made the family applicable.&lt;/p&gt;
&lt;p&gt;Volume came after.&lt;/p&gt;
&lt;p&gt;That distinction between solving a problem and recognising a family from constraints is exactly what eventually shaped how I built Codeintuition.&lt;/p&gt;
&lt;h4 id=&quot;3-don-t-retest-with-the-same-problem&quot;&gt;3. Don't retest with the same problem&lt;/h4&gt;
&lt;p&gt;A retest on a problem already seen, even one that was failed, measured recall instead of reasoning.&lt;/p&gt;
&lt;p&gt;The whole point of the diagnostic was novel problem performance.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-the-failure-modes-told-me&quot;&gt;What the failure modes told me&lt;/h3&gt;
&lt;p&gt;Most engineers running a version of this diagnostic don't pass three for three on the first attempt.&lt;/p&gt;
&lt;p&gt;The first attempt for me wasn't clean either.&lt;/p&gt;
&lt;p&gt;The diagnoses to read off the result:&lt;/p&gt;
&lt;h4 id=&quot;one-family-failed&quot;&gt;One family failed&lt;/h4&gt;
&lt;p&gt;The pattern was understood at a surface level but the identification triggers and the construction skeleton hadn't been internalised.&lt;/p&gt;
&lt;p&gt;Fix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;revisit foundational material&lt;/li&gt;
&lt;li&gt;retest with a different unseen problem&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;two-families-failed&quot;&gt;Two families failed&lt;/h4&gt;
&lt;p&gt;Usually:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;One strong area with over practised volume, shallow gaps everywhere else.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Common for engineers who spent months on one topic because the work felt productive.&lt;/p&gt;
&lt;p&gt;Fix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;broaden coverage&lt;/li&gt;
&lt;li&gt;spend focused time on the families where the depth is thin&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;all-three-failed&quot;&gt;All three failed&lt;/h4&gt;
&lt;p&gt;Method gap, not talent gap.&lt;/p&gt;
&lt;p&gt;Too much:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;solving familiar problems&lt;/li&gt;
&lt;li&gt;repetition&lt;/li&gt;
&lt;li&gt;memorisation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not enough:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;recognition&lt;/li&gt;
&lt;li&gt;transfer&lt;/li&gt;
&lt;li&gt;unseen problems&lt;/li&gt;
&lt;li&gt;interview shaped conditions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fix is narrower than:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Work harder.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-i-wish-someone-had-handed-me-earlier&quot;&gt;What I wish someone had handed me earlier&lt;/h3&gt;
&lt;p&gt;The difference between the score and the diagnostic was the difference between an opaque feeling and an action plan.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Maybe 70 percent ready&quot; was anxiety.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;Two passed, the DP subsequence family failed because the invariant for non contiguous selections wasn't internalised yet&quot; was what to do on Wednesday.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If family coverage is the bottleneck rather than conditions, the algorithms learning path on Codeintuition is one place to fill the gap before retesting.&lt;/p&gt;
&lt;p&gt;The diagnostic itself doesn't depend on it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The point is the test, not the source of the problems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I originally wrote a longer version on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.codeintuition.io/blogs/am-i-ready-for-faang&quot;&gt;my own blog&lt;/a&gt; that includes the per family signal breakdowns and diagnosis trees for the most common failure modes.&lt;/p&gt;
&lt;p&gt;What was the moment that made the readiness question itself feel like the wrong question, and what did you start measuring instead?&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/17163/how-to-know-if-youre-ready-for-faang-interviews</guid>
<pubDate>Sat, 09 May 2026 12:57:37 +0000</pubDate>
</item>
<item>
<title>LeetCode Milestone!!!</title>
<link>https://coderlegion.com/14519/leetcode-milestone</link>
<description>&lt;p&gt;1400+ leetcode solved!!!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14519/leetcode-milestone</guid>
<pubDate>Sat, 11 Apr 2026 17:07:12 +0000</pubDate>
</item>
<item>
<title>960. Delete Columns to Make Sorted III</title>
<link>https://coderlegion.com/13840/960-delete-columns-to-make-sorted-iii</link>
<description>&lt;p&gt;&lt;em&gt;In the last post, I discussed how recognizing patterns is important in solving coding challenges and discussed the Longest Increasing Subsequence. I shall talk about applying the basics in this problem to solve one variation of LIS. Follow me here, on Twitter, LinkedIn or Github for more.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;br&gt;
You are given an array of n strings strs, all of the same length.&lt;/p&gt;
&lt;p&gt;We may choose any deletion indices, and we delete all the characters in those indices for each string.&lt;/p&gt;
&lt;p&gt;For example, if we have strs = [&quot;abcdef&quot;,&quot;uvwxyz&quot;] and deletion indices {0, 2, 3}, then the final array after deletions is [&quot;bef&quot;, &quot;vyz&quot;].&lt;/p&gt;
&lt;p&gt;Suppose we chose a set of deletion indices answer such that after deletions, the final array has every string (row) in lexicographic order. (i.e., (strs[0][0] &amp;lt;= strs[0][1] &amp;lt;= ... &amp;lt;= strs[0][strs[0].length - 1]), and (strs[1][0] &amp;lt;= strs[1][1] &amp;lt;= ... &amp;lt;= strs[1][strs[1].length - 1]), and so on). Return the minimum possible value of answer.length.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: strs = [&quot;babca&quot;,&quot;bbazb&quot;]&lt;br&gt;
Output: 3&lt;br&gt;
Explanation: After deleting columns 0, 1, and 4, the final array is strs = [&quot;bc&quot;, &quot;az&quot;].&lt;br&gt;
Both these rows are individually in lexicographic order (ie. strs[0][0] &amp;lt;= strs[0][1] and strs[1][0] &amp;lt;= strs[1][1]).&lt;br&gt;
Note that strs[0] &amp;gt; strs[1] - the array strs is not necessarily in lexicographic order.&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: strs = [&quot;edcba&quot;]&lt;br&gt;
Output: 4&lt;br&gt;
Explanation: If we delete less than 4 columns, the only row will not be lexicographically sorted.&lt;br&gt;
Example 3:&lt;/p&gt;
&lt;p&gt;Input: strs = [&quot;ghi&quot;,&quot;def&quot;,&quot;abc&quot;]&lt;br&gt;
Output: 0&lt;br&gt;
Explanation: All rows are already lexicographically sorted.&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;n == strs.length&lt;br&gt;
1 &amp;lt;= n &amp;lt;= 100&lt;br&gt;
1 &amp;lt;= strs[i].length &amp;lt;= 100&lt;br&gt;
strs[i] consists of lowercase English letters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We apply the same principle in LIS with the double for loops.The only difference is we check all the characters in one column are less than or equal to all the characters in the other. We do this as we are trying to maximize the number of columns to be deleted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public int minDeletionSize(String[] strs) {
     int length=strs[0].length();      
     int [] dp=new  int[length];          
     Arrays.fill(dp,1);
     for(int col=1;col&amp;lt;length;col++){       
      for(int j=0;j&amp;lt;col;j++){
        int numOfRows=0; 
        for(int i=0;i&amp;lt;strs.length;i++){
         char curr=strs[i].charAt(col);   
         char prev=strs[i].charAt(j);
         if(prev&amp;gt;curr) break;
         else numOfRows++;  
         }
         if(numOfRows==strs.length &amp;amp;&amp;amp; dp[col]&amp;lt;=dp[j])dp[col]=dp[j]+1;          
        }
      }  
     Arrays.sort(dp);
     return length-dp[dp.length-1]; 
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C#&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Solution {
    public int MinDeletionSize(string[] strs) {
      int length=strs[0].Length;      
     int [] dp=new  int[length];          
     Array.Fill(dp,1);
     for(int col=1;col&amp;lt;length;col++){       
      for(int j=0;j&amp;lt;col;j++){
        int numOfRows=0; 
        for(int i=0;i&amp;lt;strs.Length;i++){
         char curr=strs[i][col];   
         char prev=strs[i][j];
         if(prev&amp;gt;curr) break;
         else numOfRows++;  
         }
         if(numOfRows==strs.Length &amp;amp;&amp;amp; dp[col]&amp;lt;=dp[j])dp[col]=dp[j]+1;          
        }
      }  
     Array.Sort(dp);
     return length-dp[dp.Length-1];    
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C++&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int minDeletionSize(vector&amp;lt;string&amp;gt;&amp;amp; strs) {
     string first=strs[0];
     int length=first.size();      
     vector&amp;lt;int&amp;gt; dp;          
     for(int i=0;i&amp;lt;length;i++)dp.push_back(1);
     for(int col=1;col&amp;lt;length;col++){       
      for(int j=0;j&amp;lt;col;j++){
        int numOfRows=0; 
        for(int i=0;i&amp;lt;strs.size();i++){
         string str= strs[i];  
         char curr=str[col];   
         char prev=str[j];
         if(prev&amp;gt;curr) break;
         else numOfRows++;  
         }
         if(numOfRows==strs.size() &amp;amp;&amp;amp; dp[col]&amp;lt;=dp[j])dp[col]=dp[j]+1;          
        }
      }  
     std::sort(dp.begin(),dp.end());
     return length-dp[dp.size()-1];    
    }
};
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/13840/960-delete-columns-to-make-sorted-iii</guid>
<pubDate>Wed, 01 Apr 2026 21:03:23 +0000</pubDate>
</item>
<item>
<title>300. Longest Increasing Subsequence</title>
<link>https://coderlegion.com/13148/300-longest-increasing-subsequence</link>
<description>&lt;p&gt;&lt;em&gt;A good way to master coding challenges is to recognize patterns. A good way to recognize patterns is to understand the basics. This may involve understanding one core problem and learning to apply it. Think of it as similar to learning to crawl before learning to walk and then learning to run. An example core problem is the Longest Increasing Subsequence. Variations of this problem are frequently asked. Follow me here, on Twitter, LinkedIn or Github for more.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Given an integer array nums, return the length of the longest strictly increasing subsequence.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: nums = [10,9,2,5,3,7,101,18]&lt;br&gt;
Output: 4&lt;br&gt;
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: nums = [0,1,0,3,2,3]&lt;br&gt;
Output: 4&lt;br&gt;
Example 3:&lt;/p&gt;
&lt;p&gt;Input: nums = [7,7,7,7,7,7,7]&lt;br&gt;
Output: 1&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;1 &amp;lt;= nums.length &amp;lt;= 2500&lt;br&gt;
-104 &amp;lt;= nums[i] &amp;lt;= 104&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To solve, we use dynamic programming. We build a 1-dimensional array lengths to store the maximum length of the subsequences at each index and initialize it so that each value is 1.&lt;br&gt;
Then we use two for loops. The first starts at the second index (index 1 in some programming languages) and iterates forward until the end of the array. We shall refer to it as i .&lt;br&gt;
The second starts at the first index (index 0 in some languages) and iterates forward until it reaches i. We shall refer to it as j. If the number found at j is less than that found at i and lengths[i] is less than or equal to lengths[j], we recalculate the value at lengths[i] as lengths[i]=lengths[j]+1.&lt;br&gt;
On finishing the double for loop,we sort lengths and return the last value which is the largest.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public int lengthOfLIS(int[] nums) {
     int [] lengths= new int[nums.length];
     for(int i=0;i&amp;lt;lengths.length;i++)lengths[i]=1;
     for(int i=1;i&amp;lt;nums.length;i++){
      for(int j=0;j&amp;lt;i;j++){
       if(nums[j]&amp;lt;nums[i] &amp;amp;&amp;amp; lengths[i]&amp;lt;=lengths[j]) lengths[i]=lengths[j]+1;
      }
     }
     Arrays.sort(lengths);
     return lengths[lengths.length-1];
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C#&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Solution {
    public int LengthOfLIS(int[] nums) {
     int [] lengths= new int[nums.Length];
     int max=0;
     for(int i=0;i&amp;lt;lengths.Length;i++)lengths[i]=1;
     for(int i=1;i&amp;lt;nums.Length;i++){
      for(int j=0;j&amp;lt;i;j++){
       if(nums[j]&amp;lt;nums[i] &amp;amp;&amp;amp; lengths[i]&amp;lt;=lengths[j]) lengths[i]=lengths[j]+1;
      }
     }
     for(int i=0;i&amp;lt;lengths.Length;i++)max=Math.Max(max,lengths[i]);
     return max;   
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C++&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int lengthOfLIS(vector&amp;lt;int&amp;gt;&amp;amp; nums) {
     vector&amp;lt;int&amp;gt;lengths;
     int max=0;
     for(int i=0;i&amp;lt;nums.size();i++)lengths.push_back(1);
     for(int i=1;i&amp;lt;nums.size();i++){
      for(int j=0;j&amp;lt;i;j++){
       if(nums.at(j)&amp;lt;nums.at(i) &amp;amp;&amp;amp; lengths.at(i)&amp;lt;=lengths.at(j)) lengths.at(i)=lengths.at(j)+1;
      }
     }
     for(int i=0;i&amp;lt;lengths.size();i++)max=std::max(max,lengths.at(i)); 
     return max;   
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Javascript&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var lengthOfLIS = function(nums) {
 const lengths= [];
 let max=0;
 for(let i=0;i&amp;lt;nums.length;i++)lengths.push(1);
 for(let i=1;i&amp;lt;nums.length;i++){
  for(let j=0;j&amp;lt;i;j++){
   if(nums[j]&amp;lt;nums[i] &amp;amp;&amp;amp; lengths[i]&amp;lt;=lengths[j]) lengths[i]=lengths[j]+1;
  }
 }
 for(let i=0;i&amp;lt;lengths.length;i++)max=Math.max(lengths[i],max);
 return max;
};
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/13148/300-longest-increasing-subsequence</guid>
<pubDate>Wed, 18 Mar 2026 18:26:42 +0000</pubDate>
</item>
<item>
<title>If You’re a Beginner in Tech, Read This Before Choosing DSA, Web Dev or AI</title>
<link>https://coderlegion.com/12068/if-youre-a-beginner-in-tech-read-this-before-choosing-dsa-web-dev-or-ai</link>
<description>&lt;h2 id=&quot;introduction-the-beginner-s-confusion&quot;&gt;Introduction: The Beginner’s Confusion&lt;/h2&gt;
&lt;p&gt;If you’re new to tech, you’ve probably heard these three words everywhere:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DSA. Web Development. AI.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Everyone online has an opinion.&lt;br&gt;
One person says: “&lt;em&gt;Do DSA first.&lt;/em&gt;”&lt;br&gt;
Another says: “&lt;em&gt;Build projects, do web dev&lt;/em&gt;.”&lt;br&gt;
Another says: “&lt;em&gt;AI is the future, learn AI now.&lt;/em&gt;”&lt;/p&gt;
&lt;p&gt;As a beginner, this creates confusion, pressure, and fear of choosing the “wrong path.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here’s the truth:&lt;/strong&gt;&lt;br&gt;
There is no single correct answer, but there is a smart order.&lt;/p&gt;
&lt;p&gt;This blog will help you understand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What each field actually means&lt;/li&gt;
&lt;li&gt;What beginners think they are &lt;/li&gt;
&lt;li&gt;What beginners should learn first &lt;/li&gt;
&lt;li&gt;How to choose based on your goals&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;understanding-the-three-paths&quot;&gt;Understanding the Three Paths&lt;/h2&gt;
&lt;h3 id=&quot;1-dsa-data-structures-amp-algorithms-&quot;&gt;1. DSA (Data Structures &amp;amp; Algorithms)&lt;/h3&gt;
&lt;p&gt;What it really is:&lt;br&gt;
Logical problem-solving using arrays, strings, recursion, trees, graphs, and algorithms.&lt;/p&gt;
&lt;p&gt;What beginners think:&lt;br&gt;
&lt;em&gt;“DSA = coding interviews.”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reality:&lt;br&gt;
DSA trains your thinking, not just your coding.&lt;/p&gt;
&lt;p&gt;It builds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Logical reasoning&lt;/li&gt;
&lt;li&gt;Problem-solving mindset&lt;/li&gt;
&lt;li&gt;Pattern recognition&lt;/li&gt;
&lt;li&gt;Code efficiency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;DSA doesn’t make apps.&lt;br&gt;
DSA doesn’t build websites.&lt;br&gt;
DSA builds brains.&lt;/p&gt;
&lt;h3 id=&quot;2-web-development&quot;&gt;2. Web Development&lt;/h3&gt;
&lt;p&gt;What it really is:&lt;br&gt;
Building real products: websites, apps, platforms, dashboards, systems.&lt;/p&gt;
&lt;p&gt;What beginners think:&lt;br&gt;
“&lt;em&gt;Web dev = HTML + CSS only.”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reality:&lt;br&gt;
Web dev teaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System thinking&lt;/li&gt;
&lt;li&gt;Real-world problem solving&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;li&gt;Authentication&lt;/li&gt;
&lt;li&gt;Architecture&lt;/li&gt;
&lt;li&gt;Debugging&lt;/li&gt;
&lt;li&gt;Product building&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Web dev builds visible results.&lt;/p&gt;
&lt;h3 id=&quot;3-ai-machine-learning&quot;&gt;3. AI / Machine Learning&lt;/h3&gt;
&lt;p&gt;What it really is:&lt;br&gt;
Math + data + programming + statistics + models + logic.&lt;/p&gt;
&lt;p&gt;What beginners think:&lt;br&gt;
&lt;em&gt;“AI = using ChatGPT or tools.”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Reality:&lt;br&gt;
AI requires:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Programming foundations&lt;/li&gt;
&lt;li&gt;Data handling&lt;/li&gt;
&lt;li&gt;Algorithms&lt;/li&gt;
&lt;li&gt;Math&lt;/li&gt;
&lt;li&gt;Probability&lt;/li&gt;
&lt;li&gt;Logic&lt;/li&gt;
&lt;li&gt;Structured thinking&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI is not beginner-friendly without foundations.&lt;/p&gt;
&lt;p&gt;The Biggest Lie Beginners Are Told&lt;/p&gt;
&lt;p&gt;&lt;em&gt;“You must choose one path at the start.”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is wrong.&lt;/p&gt;
&lt;p&gt;Tech is not a single-lane road.&lt;br&gt;
It’s a layered system.&lt;/p&gt;
&lt;p&gt;You don’t choose one,&lt;br&gt;
You build in layers.&lt;/p&gt;
&lt;h2 id=&quot;the-smart-learning-order-beginner-friendly-&quot;&gt;The Smart Learning Order (Beginner Friendly)&lt;/h2&gt;
&lt;p&gt;Here’s the most practical path for beginners:&lt;/p&gt;
&lt;h3 id=&quot;step-1-programming-fundamentals&quot;&gt;Step 1: Programming Fundamentals&lt;/h3&gt;
&lt;p&gt;Before anything else:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One language (Python / JavaScript / Java / C++)  &lt;/li&gt;
&lt;li&gt;Variables  &lt;/li&gt;
&lt;li&gt;Loops  &lt;/li&gt;
&lt;li&gt;Conditions  &lt;/li&gt;
&lt;li&gt;Functions  &lt;/li&gt;
&lt;li&gt;Basic logic  &lt;/li&gt;
&lt;li&gt;Input/output  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No foundation = no growth.&lt;/p&gt;
&lt;h3 id=&quot;step-2-basic-dsa-problem-solving&quot;&gt;Step 2: Basic DSA + Problem Solving&lt;/h3&gt;
&lt;p&gt;Not hardcore competitive coding — but:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Arrays  &lt;/li&gt;
&lt;li&gt;Strings  &lt;/li&gt;
&lt;li&gt;Basic recursion  &lt;/li&gt;
&lt;li&gt;Searching  &lt;/li&gt;
&lt;li&gt;Sorting  &lt;/li&gt;
&lt;li&gt;Pattern problems  &lt;/li&gt;
&lt;li&gt;Logical thinking&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This builds thinking ability.&lt;/p&gt;
&lt;h3 id=&quot;step-3-web-development-project-building-phase-&quot;&gt;Step 3: Web Development (Project Building Phase)&lt;/h3&gt;
&lt;p&gt;Now start building:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTML, CSS, JS  &lt;/li&gt;
&lt;li&gt;Frontend frameworks  &lt;/li&gt;
&lt;li&gt;Backend basics  &lt;/li&gt;
&lt;li&gt;Databases  &lt;/li&gt;
&lt;li&gt;APIs  &lt;/li&gt;
&lt;li&gt;Authentication  &lt;/li&gt;
&lt;li&gt;Real projects  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This builds confidence + skill + portfolio.&lt;/p&gt;
&lt;h3 id=&quot;step-4-ai-ml-when-foundation-is-ready-&quot;&gt;Step 4: AI / ML (When Foundation Is Ready)&lt;/h3&gt;
&lt;p&gt;Now AI makes sense:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data handling  &lt;/li&gt;
&lt;li&gt;Python  &lt;/li&gt;
&lt;li&gt;Logic  &lt;/li&gt;
&lt;li&gt;Algorithms  &lt;/li&gt;
&lt;li&gt;Math foundations  &lt;/li&gt;
&lt;li&gt;Real understanding of models  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without foundation, AI becomes copy-paste learning.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;so-what-should-beginners-actually-learn-first-&quot;&gt;So What Should Beginners Actually Learn First?&lt;/h3&gt;
&lt;p&gt;The honest answer:&lt;/p&gt;
&lt;p&gt;Learn how to think, then learn how to build, then learn how to specialize.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;In simple words:&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learn programming basics  &lt;/li&gt;
&lt;li&gt;Learn problem-solving (DSA basics)  &lt;/li&gt;
&lt;li&gt;Build real projects (Web Dev)  &lt;/li&gt;
&lt;li&gt;Then choose specialization (AI, ML, Data, DevOps, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Different Goals = Different Focus&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If your goal is jobs:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Programming&lt;/li&gt;
&lt;li&gt;DSA&lt;/li&gt;
&lt;li&gt;Web dev projects&lt;/li&gt;
&lt;li&gt;System understanding&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;If your goal is startups:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web dev&lt;/li&gt;
&lt;li&gt;Product building&lt;/li&gt;
&lt;li&gt;Systems&lt;/li&gt;
&lt;li&gt;Scaling thinking&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;If your goal is research/AI:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Math&lt;/li&gt;
&lt;li&gt;Programming&lt;/li&gt;
&lt;li&gt;Algorithms&lt;/li&gt;
&lt;li&gt;Data&lt;/li&gt;
&lt;li&gt;Then AI/ML&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Final Truth Beginners Need to Hear&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You don’t need to rush into AI.&lt;br&gt;
You don’t need to master DSA on day one.&lt;br&gt;
You don’t need to build startups immediately.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What you need is:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clarity&lt;/li&gt;
&lt;li&gt;Consistency&lt;/li&gt;
&lt;li&gt;Foundations&lt;/li&gt;
&lt;li&gt;Patience&lt;/li&gt;
&lt;li&gt;Daily progress&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because in tech:&lt;/p&gt;
&lt;p&gt;Skills compound. Knowledge stacks. Progress multiplies.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;closing-thought&quot;&gt;Closing Thought&lt;/h3&gt;
&lt;p&gt;If you’re a beginner and confused between DSA, Web Dev, and AI, remember this:&lt;/p&gt;
&lt;p&gt;Strong foundations create strong careers.&lt;br&gt;
Strong thinking creates strong coders.&lt;br&gt;
Strong systems create strong builders.&lt;/p&gt;
&lt;p&gt;Start simple.&lt;br&gt;
Build slowly.&lt;br&gt;
Grow deeply.&lt;br&gt;
And choose smartly.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/12068/if-youre-a-beginner-in-tech-read-this-before-choosing-dsa-web-dev-or-ai</guid>
<pubDate>Mon, 23 Feb 2026 06:15:47 +0000</pubDate>
</item>
<item>
<title>LeetCode #1458.Max Dot Product of Two Subsequences</title>
<link>https://coderlegion.com/11062/leetcode-1458-max-dot-product-of-two-subsequences</link>
<description>&lt;p&gt;&lt;em&gt;Many engineers find leetcode problems hard and not a reflection of the actual work to do. I do have some sympathy for this view. Nonetheless, I do think that these questions can help practice and build programming skills. Due to this and the fact that these prop up in interviews, I will post discussions of these from time to time.Follow me on twitter,github or Linkedin for these and more.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Given two arrays nums1 and nums2.&lt;/p&gt;
&lt;p&gt;Return the maximum dot product between non-empty subsequences of nums1 and nums2 with the same length.&lt;/p&gt;
&lt;p&gt;A subsequence of an array is a new array which is formed from the original array by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, [2,3,5] is a subsequence of [1,2,3,4,5] while [1,5,3] is not).&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: nums1 = [2,1,-2,5], nums2 = [3,0,-6]&lt;br&gt;
Output: 18&lt;br&gt;
Explanation: Take subsequence [2,-2] from nums1 and subsequence [3,-6] from nums2.&lt;br&gt;
Their dot product is (2&lt;em&gt;3 + (-2)&lt;/em&gt;(-6)) = 18.&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: nums1 = [3,-2], nums2 = [2,-6,7]&lt;br&gt;
Output: 21&lt;br&gt;
Explanation: Take subsequence [3] from nums1 and subsequence [7] from nums2.&lt;br&gt;
Their dot product is (3*7) = 21.&lt;br&gt;
Example 3:&lt;/p&gt;
&lt;p&gt;Input: nums1 = [-1,-1], nums2 = [1,1]&lt;br&gt;
Output: -1&lt;br&gt;
Explanation: Take subsequence [-1] from nums1 and subsequence [1] from nums2.&lt;br&gt;
Their dot product is -1.&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;1 &amp;lt;= nums1.length, nums2.length &amp;lt;= 500&lt;br&gt;
-1000 &amp;lt;= nums1[i], nums2[i] &amp;lt;= 1000&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Discussion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I was stumped by this question. I read the hints and somebody mentioned that it was similar to Longest Common Subsequence. That got me thinking. I had to use a 2-D dp array to get the result.&lt;/p&gt;
&lt;p&gt;I also had to keep track of the multiplication of each number from each array along with the values from the previous row,current column and the previous column and current row.For edge cases I had to calculate the values for the first row and the first column.I came up with the solution below which passed all 70 cases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public int maxDotProduct(int[] nums1, int[] nums2) {
     int  [] [] dp=new int[nums1.length] [nums2.length];   
     int firstRowMax=nums1[0]*nums2[0];
     int answer=0;
     dp[0][0]=firstRowMax;
     for(int j=1;j&amp;lt;nums2.length;j++){
      int current=nums1[0]*nums2[j];
      firstRowMax=Math.max(firstRowMax,current);
      dp[0][j]=firstRowMax;
     }
     answer=firstRowMax;
     for(int i=1;i&amp;lt;nums1.length;i++){
      for(int j=0;j&amp;lt;nums2.length;j++){
        int current=nums1[i]*nums2[j];
        if(j==0) dp[i][j]=Math.max(dp[i-1][j],current);   
        else
         dp[i][j]=Math.max(dp[i-1][j-1]+current,Math.max(current,Math.max(dp[i-1][j],dp[i][j-1])));
        answer=Math.max(answer,dp[i][j]);
      }  
     }
     return answer;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/11062/leetcode-1458-max-dot-product-of-two-subsequences</guid>
<pubDate>Wed, 04 Feb 2026 17:37:05 +0000</pubDate>
</item>
<item>
<title>Building Queue From Stack</title>
<link>https://coderlegion.com/5202/building-queue-from-stack</link>
<description>&lt;p&gt;Imagine your interviewer asks for a queue but with a twist: you can only use stacks. &lt;/p&gt;
&lt;p&gt;It’s actually a small shift in approach that reveals how data structure thinking and algorithmic trade offs make you a stronger engineer. &lt;/p&gt;
&lt;p&gt;Read on, this one problem has shown up in interviews, production bug fixes, and test cases for people who later got promoted for thinking clearly under constraint.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/coding-nexus/dsa-lecture-38-building-a-queue-with-stacks-6a7c06606ccc&quot;&gt;Building a Queue using Two Stacks&lt;/a&gt; is a classic DSA puzzle that’s deceptively practical. It’s not just about solving the puzzle, it’s about demonstrating these career making skills:&lt;/p&gt;
&lt;p&gt;1 : converting constraints into elegant designs,&lt;br&gt;
2 : reasoning about amortized complexity (not just worst case noise),&lt;br&gt;
3 : writing predictable, maintainable code that can be explained in an interview or to your teammates.&lt;/p&gt;
&lt;p&gt;Stacks are LIFO, queues are FIFO, that’s the core mismatch. But by pairing two stacks and moving elements between them on demand, we get the FIFO behavior for free, while keeping operations simple. &lt;/p&gt;
&lt;p&gt;How to approach ? : do most of the work lazily (only when needed). This gives amortized O(1) time for both enqueue and dequeue, which is often good enough in interviews and many real world scenarios.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class QueueUsingStacks:
    &quot;&quot;&quot;
    Queue implemented with two stacks (lists).
    - push_stack: where we push new elements (enqueue)
    - pop_stack: where we pop elements for dequeue/peek
    Amortized costs: enqueue O(1), dequeue amortized O(1).
    &quot;&quot;&quot;
    def __init__(self):
        self.push_stack = []
        self.pop_stack = []

    def _transfer_if_needed(self):
        &quot;&quot;&quot;Move everything from push_stack to pop_stack if pop_stack is empty.
        This reverses order so front of queue is at the end of pop_stack.
        Each element moves at most once =&amp;gt; amortized O(1) per op.
        &quot;&quot;&quot;
        if not self.pop_stack:
            while self.push_stack:
                self.pop_stack.append(self.push_stack.pop())

    def enqueue(self, x):
        &quot;&quot;&quot;Add x to the end of the queue. O(1).&quot;&quot;&quot;
        self.push_stack.append(x)

    def dequeue(self):
        &quot;&quot;&quot;Remove and return the front element. Raises IndexError if empty.&quot;&quot;&quot;
        self._transfer_if_needed()
        if not self.pop_stack:
            raise IndexError(&quot;dequeue from empty queue&quot;)
        return self.pop_stack.pop()

    def peek(self):
        &quot;&quot;&quot;Return front element without removing it. Raises IndexError if empty.&quot;&quot;&quot;
        self._transfer_if_needed()
        if not self.pop_stack:
            raise IndexError(&quot;peek from empty queue&quot;)
        return self.pop_stack[-1]

    def is_empty(self):
        &quot;&quot;&quot;True if queue has no elements.&quot;&quot;&quot;
        return not (self.push_stack or self.pop_stack)

    def __len__(self):
        &quot;&quot;&quot;Total number of elements in the queue.&quot;&quot;&quot;
        return len(self.push_stack) + len(self.pop_stack)


# ---- clear demo with labeled prints ----
if __name__ == &quot;__main__&quot;:
    q = QueueUsingStacks()
    q.enqueue(10)
    q.enqueue(20)
    q.enqueue(30)

    print(&quot;dequeue -&amp;gt;&quot;, q.dequeue())   # -&amp;gt; 10
    q.enqueue(40)

    print(&quot;peek    -&amp;gt;&quot;, q.peek())      # -&amp;gt; 20
    print(&quot;len     -&amp;gt;&quot;, len(q))        # -&amp;gt; 3

    print(&quot;draining queue:&quot;)
    while not q.is_empty():
        print(q.dequeue())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you found this helpful, like or share so your network can pick up a neat DSA approach. Read more on DSA with me &lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/@yogicode1&quot;&gt;here.&lt;/a&gt; &lt;/p&gt;
</description>
<category>Data Structures</category>
<guid isPermaLink="true">https://coderlegion.com/5202/building-queue-from-stack</guid>
<pubDate>Fri, 05 Sep 2025 18:44:00 +0000</pubDate>
</item>
<item>
<title>Understanding Recursion</title>
<link>https://coderlegion.com/4913/understanding-recursion</link>
<description>&lt;p&gt;Have you ever seen those Russian dolls, where one doll opens to reveal a smaller one inside, and then another smaller one, and so on? Recursion in programming is a bit like that. It’s when a function calls itself to solve a smaller piece of the problem until it reaches the simplest version, which can be solved directly.&lt;/p&gt;
&lt;p&gt;Let’s take a simple real-world example: &lt;strong&gt;printing a list of names from last to first.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Recursive function to print names in reverse order
def print_names_reverse(names, index):
    # Base case: stop when we reach the beginning
    if index &amp;lt; 0:
        return
    
    # Print the current name
    print(names[index])
    
    # Recursive call: move to the previous name
    print_names_reverse(names, index - 1)

# Sample list
names_list = [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;, &quot;Diana&quot;]

# Start recursion from the last index
print_names_reverse(names_list, len(names_list) - 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;how-it-works-step-by-step-&quot;&gt;How it works step by step:&lt;/h3&gt;
&lt;p&gt;We start at the last name (“Diana”).&lt;/p&gt;
&lt;p&gt;The function prints it, then calls itself for the previous name.&lt;/p&gt;
&lt;p&gt;This keeps happening until we reach the first name.&lt;/p&gt;
&lt;p&gt;Once the first name is printed, the function stops — this stopping point is called the base case.&lt;/p&gt;
&lt;p&gt;Notice how the function keeps calling itself, but each time it’s a slightly smaller problem. That’s recursion in its simplest form.&lt;/p&gt;
&lt;p&gt;Recursion can look magical, but it’s all about breaking a problem into smaller pieces. Once you get the hang of it, you’ll see it’s a very natural way to solve problems — especially ones that repeat themselves.&lt;/p&gt;
&lt;p&gt;Sometimes, recursion can end up repeating the same work for different parts of a problem. This is where a concept called memoization comes in — it’s a clever way to remember results so you don’t redo work unnecessarily.&lt;/p&gt;
&lt;p&gt;Curious about making recursion &lt;strong&gt;super efficient&lt;/strong&gt; and avoiding repeated work? Check out my &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/coding-nexus/dsa-lecture-30-recursion-classic-problem-with-memoization-1a0ec3233503?sk=1dff56278298f131a908e601ebd6055d&quot;&gt;Medium article on recursion with memoization&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
 to see the next level in action! (Free link attached for you)&lt;/p&gt;
</description>
<category>Getting Started</category>
<guid isPermaLink="true">https://coderlegion.com/4913/understanding-recursion</guid>
<pubDate>Fri, 22 Aug 2025 09:29:05 +0000</pubDate>
</item>
<item>
<title>From Struggle to Solution-An Approach to Solving a programming problem using #650.Leetcode's 2 keys</title>
<link>https://coderlegion.com/3891/from-struggle-solution-approach-solving-programming-problem-using-650-leetcodes-keys</link>
<description>&lt;p&gt;&lt;strong&gt;1.Introduction&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt; I regularly practice leetcode if not always on a daily basis. The reasons that I do this are: to keep my coding skills sharp, to deepen my understanding of algorithms and data structures and to prepare for coding challenges and technical interviews. Recently, I solved Leetcode's #650-the 2 keys keyboard using dynamic programming and I have decided to share my approach to solving this problem.Below is a description of the problem.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;2 Keys Keyboard&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There is only one character 'A' on the screen of a notepad. You can perform one of two operations on this notepad for each step:&lt;/p&gt;
&lt;p&gt;Copy All: You can copy all the characters present on the screen (a partial copy is not allowed).&lt;br&gt;
Paste: You can paste the characters which are copied last time.&lt;br&gt;
Given an integer n, return the minimum number of operations to get the character 'A' exactly n times on the screen.&lt;/p&gt;
&lt;p&gt;Example 1:&lt;/p&gt;
&lt;p&gt;Input: n = 3&lt;br&gt;
Output: 3&lt;br&gt;
Explanation: Initially, we have one character 'A'.&lt;br&gt;
In step 1, we use Copy All operation.&lt;br&gt;
In step 2, we use Paste operation to get 'AA'.&lt;br&gt;
In step 3, we use Paste operation to get 'AAA'.&lt;br&gt;
Example 2:&lt;/p&gt;
&lt;p&gt;Input: n = 1&lt;br&gt;
Output: 0&lt;/p&gt;
&lt;p&gt;Constraints:&lt;/p&gt;
&lt;p&gt;1 &amp;lt;= n &amp;lt;= 1000&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.First Thoughts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt; I read the problem and the topics it was listed under. It was listed as Math and Dynamic Programming. My understanding was that there were 2 types of operations: a copy and paste and a paste. I was at first baffled and confused about where to start. I mean how could I minimize the number of operations to achieve this task? The fact that it was listed as dynamic programming gave me an idea-use a 1-d array to store the results of subproblems but I was still baffled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.Attempts &amp;amp; Failures&lt;/strong&gt;&lt;br&gt;
  I read the hints. Only 1 was provided. It said &quot;How many characters may be there in the clipboard at the last step if n = 3? n = 7? n = 10? n = 24?&quot;. This confused me more. I decided to try something. I could not come up with anything so I decided to reread the problem. On doing so, I realized my first error. &lt;em&gt;2 types of operations were allowed: a copy all and a paste&lt;/em&gt;.This helped. I did some thinking and realized that for a final string of 6,I could do a copy and paste of 3 characters and ditto for a string of 4 characters. It was progress but unfortunately not enough. I decided to read the discussion. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2 observations.
If n is a prime=&amp;gt; ans=n
If n=2^k=&amp;gt; ans=2*k
Something to do with prime factorization, maybe Sieve method can play again, not very necessary.
Have a nice day.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was helpful but still I struggled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. The Breakthrough&lt;/strong&gt;&lt;br&gt;
 I decided to give it a rest and to come back to it later. I was eating lunch and my mind thought about it. I had a realization!!! If the final string was 1000 characters, I could minimize by performing a copy and paste on a string of 500 problems. If it was prime, I could perform a copy and paste of 1 character n times. If it was a string of 27 characters, I could copy a string of 9 characters and paste it twice. So I found a solution. Create a 1-d array of length n+1.Iterate from the index 2 to the end of the array,if the current index is prime, record its number.Otherwise,get its greatest factor,go to that position in the array and calculate the current value by adding the dp value of the highest factor and the quotient of the current value/highest factor.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if(isPrime(i) dp[i]=i;
else dp[i]=dp[highestFactor(i)]+(i/highestFactor(i)).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; With this in mind, I attempted a solution. I attempted to do this in Java. Here was my first solution.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public int minSteps(int n) {
      int []dp= new int[n+1];
      for(int i=2;i&amp;lt;n+1;i++){
       if(isPrime(i))dp[i]=i;
       else {
        //System.out.println
        dp[i] =dp[highestDivisor(i)]+(i/highestDivisor(i));
       } 
      }  
      return dp[n];
    }

    public int highestDivisor(int n) {
        if (n &amp;lt;= 1) return -1; // No proper divisor exists for 0 or 1

        for (int i = n / 2; i &amp;gt;= 1; i--) {
            if (n % i == 0) {
                return i; // First divisor found from the top is the highest
            }
        }
        return -1; // Should never reach here for n &amp;gt; 1
    }

    public  boolean isPrime(int n) {
        if (n &amp;lt;= 1) return false; // 0 and 1 are not prime
        if (n &amp;lt;= 3) return true;  // 2 and 3 are prime
        if (n % 2 == 0 || n % 3 == 0) return false; // eliminate multiples of 2 and 3

        // Only check up to √n using 6k ± 1 optimization
        for (int i = 5; i * i &amp;lt;= n; i += 6) {
            if (n % i == 0 || n % (i + 2) == 0)
                return false;
        }
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; As part of my recent decision to try and get solutions in at least 4 languages (Java, c# ,c++ and javascript),I attempted to achieve solutions in c#,c++ and javascript. I first tried c# and the following solution was accepted.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Solution {
    public int MinSteps(int n) {
     int []dp= new int[n+1];
      for(int i=2;i&amp;lt;n+1;i++){
       if(IsPrime(i))dp[i]=i;
       else dp[i] =dp[HighestDivisor(i)]+(i/HighestDivisor(i));        
      }  
      return dp[n];
    }

    public int HighestDivisor(int n) {
        if (n &amp;lt;= 1) return -1; // No proper divisor exists for 0 or 1

        for (int i = n / 2; i &amp;gt;= 1; i--) {
            if (n % i == 0) {
                return i; // First divisor found from the top is the highest
            }
        }
        return -1; // Should never reach here for n &amp;gt; 1
    }

    public  bool IsPrime(int n) {
        if (n &amp;lt;= 1) return false; // 0 and 1 are not prime
        if (n &amp;lt;= 3) return true;  // 2 and 3 are prime
        if (n % 2 == 0 || n % 3 == 0) return false; // eliminate multiples of 2 and 3

        // Only check up to √n using 6k ± 1 optimization
        for (int i = 5; i * i &amp;lt;= n; i += 6) {
            if (n % i == 0 || n % (i + 2) == 0)
                return false;
        }
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I then attempted c++ and my solution was again accepted.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
public:
    int minSteps(int n) {
      vector&amp;lt;int&amp;gt;dp;
      for(int i=0;i&amp;lt;n+1;i++)dp.push_back(0);
      for(int i=2;i&amp;lt;n+1;i++){
       if(isPrime(i))dp[i]=i;
       else dp[i] =dp[highestDivisor(i)]+(i/highestDivisor(i));        
      }  
      return dp[n];
    }

    int highestDivisor(int n) {
        if (n &amp;lt;= 1) return -1; // No proper divisor exists for 0 or 1

        for (int i = n / 2; i &amp;gt;= 1; i--) {
            if (n % i == 0) {
                return i; // First divisor found from the top is the highest
            }
        }
        return -1; // Should never reach here for n &amp;gt; 1
    }

    bool isPrime(int n) {
        if (n &amp;lt;= 1) return false; // 0 and 1 are not prime
        if (n &amp;lt;= 3) return true;  // 2 and 3 are prime
        if (n % 2 == 0 || n % 3 == 0) return false; // eliminate multiples of 2 and 3

        // Only check up to √n using 6k ± 1 optimization
        for (int i = 5; i * i &amp;lt;= n; i += 6) {
            if (n % i == 0 || n % (i + 2) == 0)
                return false;
        }
        return true;
    }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; Feeling very proud, I decided to try in javascript, I tried the following but saw that it was rejected.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var minSteps = function(n) {
      const []dp;
      for(let i=2;i&amp;lt;n+1;i++){
       let highestDivisor=i;
       for(let j=i/2;j&amp;gt;1;j--){
        if(i%j==0){
          highestDivisor=j;
          break;  
        }
       } 
       if(highestDivisor==i)dp[i]=i; 
       else dp[i] =dp[highestDivisor]+(i/highestDivisor);        
      }  
      return dp[n];  
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; I got a runtime error and was flabbergasted. I decided to come back later and to first attempt an optimized java solution. I decided to come up with the following approach. Instead of determining if a number is prime to find the highest divisor. I used the following which was accepted.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public int minSteps(int n) {
      int []dp= new int[n+1];
      for(int i=2;i&amp;lt;n+1;i++){
       if(isPrime(i))dp[i]=i;
       else {
        //System.out.println
        dp[i] =dp[highestDivisor(i)]+(i/highestDivisor(i));
       } 
      }  
      return dp[n];
    }

    public int highestDivisor(int n) {
        if (n &amp;lt;= 1) return -1; // No proper divisor exists for 0 or 1

        for (int i = n / 2; i &amp;gt;= 1; i--) {
            if (n % i == 0) {
                return i; // First divisor found from the top is the highest
            }
        }
        return -1; // Should never reach here for n &amp;gt; 1
    }

    public  boolean isPrime(int n) {
        if (n &amp;lt;= 1) return false; // 0 and 1 are not prime
        if (n &amp;lt;= 3) return true;  // 2 and 3 are prime
        if (n % 2 == 0 || n % 3 == 0) return false; // eliminate multiples of 2 and 3

        // Only check up to √n using 6k ± 1 optimization
        for (int i = 5; i * i &amp;lt;= n; i += 6) {
            if (n % i == 0 || n % (i + 2) == 0)
                return false;
        }
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; It was accepted and was slightly better than the original. I felt happy. I decided to retry the javascript solution. I attempted to debug using print statements and realized that j was initialized as 1.5.So,I made some changes. I used j=i-1 and reran and came up with this solution which was accepted!!! I felt I was on a roll!!!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var minSteps = function(n) {
      const dp=[];
      for(let i=0;i&amp;lt;n+1;i++)dp.push(0);
      for(let i=2;i&amp;lt;n+1;i++){
       let highestDivisor=i;
       for(let j=i-1;j&amp;gt;1;j--){
        if(i%j==0){
          highestDivisor=j;
          break;  
        }
       } 
       if(highestDivisor==i)dp[i]=i; 
       else dp[i] =dp[highestDivisor]+(i/highestDivisor);        
      }
      return dp[n];  
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;5. Lessons Learned&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt; This experience has taught me/helped reinforce a number of lessons.&lt;br&gt;
1) Fully understand the problem a program/coding challenge is trying to solve by careful reading, asking the necessary questions. A problem cant be solved if you have misunderstood or dont understand it.&lt;/p&gt;
&lt;p&gt;2) Research how previous or similar problems were solved by other programmers.&lt;/p&gt;
&lt;p&gt;3) Develop a first draft solution to the problem.&lt;/p&gt;
&lt;p&gt;4) If stuck, take a break. Solutions can come when your mind is at rest.&lt;/p&gt;
&lt;p&gt;5) Optimize your solutions where possible. It can save time and memory. In production environments, this can make a noticeable difference.&lt;/p&gt;
&lt;p&gt;6) Different programming languages handle variables differently. Understand how.&lt;/p&gt;
&lt;p&gt;7) Debugging with print statements is an unsophisticated but simple way to troubleshoot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. Wrap-Up&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt; I hope my post has been helpful, please feel free to like,comment and share. Please check out my github profile for this and more solutions.It is here : &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/HectorwTT&quot;&gt;https://github.com/HectorwTT&lt;/a&gt;&lt;/p&gt;
</description>
<category>Java</category>
<guid isPermaLink="true">https://coderlegion.com/3891/from-struggle-solution-approach-solving-programming-problem-using-650-leetcodes-keys</guid>
<pubDate>Thu, 10 Jul 2025 18:41:52 +0000</pubDate>
</item>
</channel>
</rss>