<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Eda Eren - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Eda Eren - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 06 May 2026 11:21:11 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/edae/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ The Bad Website Club is Running a Free Responsive Web Design Bootcamp Based on freeCodeCamp ]]>
                </title>
                <description>
                    <![CDATA[ Hi everyone! We (Jess, Carmen, and Eda) are excited to announce the next installation of our free and online bootcamp. We support learners as they work their way through the freeCodeCamp Responsive We ]]>
                </description>
                <link>https://www.freecodecamp.org/news/bad-website-club-bootcamp-based-on-freecodecamp-rwd-cert/</link>
                <guid isPermaLink="false">69ce8df10ff860b6defc7074</guid>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bootcamp ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Eda Eren ]]>
                </dc:creator>
                <pubDate>Thu, 02 Apr 2026 15:40:33 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/9a8913d9-9bc5-4e9e-9119-1d9d429578a0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hi everyone!</p>
<p>We (Jess, Carmen, and Eda) are excited to announce the next installation of our free and online bootcamp. We support learners as they work their way through the freeCodeCamp Responsive Web Design curriculum. The bootcamp will start Friday, April 24th, and will run for 10 weeks until Friday, July 3rd.</p>
<p>Here’s what to expect:</p>
<ul>
<li><p><strong>Live Streams:</strong> We’ll be streaming Monday to Friday at 15.00 UTC (<a href="https://everytimezone.com/s/6e8e42f0">you can check your timezone here</a>), where we’ll go through the course material together on our <a href="https://www.youtube.com/@badwebsiteclub">YouTube channel</a>, on <a href="https://www.twitch.tv/jesslynnrose">Jess' Twitch channel</a> and <a href="https://www.twitch.tv/hola_soy_milk_">Carmen's Twitch channel</a>. It’s also a chance to ask your questions! If you can’t join us live, the streams will be available offline to watch later.</p>
</li>
<li><p><strong>Guest Sessions:</strong> We’ll expand on our studies with guest sessions where professionals working in software engineering and related fields will talk about their craft.</p>
</li>
<li><p><strong>Community:</strong> To support each other and work together, we have a dedicated Discord channel in the freeCodeCamp Discord server (you can join here: <a href="https://discord.gg/KVUmVXA">https://discord.gg/KVUmVXA</a>) to help you establish better connections with other learners working on freeCodeCamp material. We’ll also be including shared notes on our <a href="https://badwebsite.club">website</a>.</p>
</li>
<li><p><strong>Calendar:</strong> We have a <a href="https://badwebsite.club/calendars/spring-2026-lessons.ics">shared calendar</a> of lessons we’ll be covering with a supportive and friendly group of learners alongside you.</p>
</li>
<li><p><strong>Newsletter:</strong> There’s no signup needed, but if you want weekly emails, you can also <a href="https://buttondown.com/bad-website-club">sign up for our newsletter</a>.</p>
</li>
</ul>
<h2 id="heading-how-does-the-bootcamp-work">How Does the Bootcamp Work?</h2>
<p>Bad Website Club bootcamps have been running for a while, so if you've joined us in the past, this one is going to feel a little different.</p>
<p>As the freeCodeCamp course material has expanded, we have adapted our learning structure to reflect what’s in it.</p>
<p>We’re experimenting with a flipped classroom model, where learners do pre-reading and more solo work before classes to come into streamed sessions, prepared to support each other’s learning.</p>
<p>Also, learners will be able to contribute to shared lesson notes which we’ll be going over in unit reviews. We’ll be talking through some (not all!) of the workshop steps and offer space for Q&amp;A in our live streams.</p>
<p>The labs and certification projects need to be done as solo work, but we’ll be looking at how to approach them together on streams and covering them in Q&amp;As.</p>
<p>We’ll be moving fast, but there are no fixed deadlines! If you need more time, your progress will be saved on your freeCodeCamp account. Also, the videos and our Discord are available after the bootcamp ends.</p>
<p>Note that the bootcamp doesn't offer job placement support or 1:1 instructor support for learners due to the size and global distribution of our learners. But we’ll help you learn the skills that prepare you to pursue opportunities on your own.</p>
<h2 id="heading-who-we-are">Who We Are</h2>
<p>Bad Website Club has been running free and online developer education programs since 2020. It's run by a small volunteer team (<a href="https://jessica.tech/">Jessica Rose</a>, <a href="https://carmenh.dev/">Carmen Huidobro</a>, and <a href="https://edaeren.com/">Eda Eren</a> – also thank you to wonderful <a href="https://kirionearth.com/">Kiri</a> who made all the art!). We focus on learning and experimenting over perfection, and believe that the web can be better when it includes everyone.</p>
<p>Also, it really is free: there’s no way to pay for it.</p>
<p>If you want to join us, you can <a href="https://badwebsite.club/calendars/spring-2026-lessons.ics">download or subscribe to the full lesson calendar</a> (for Google Calendar, use the iCal feed with <a href="https://support.google.com/calendar/answer/37100?hl=en">these subscription instructions</a>), follow us on our <a href="https://www.youtube.com/@badwebsiteclub">YouTube channel</a>, on <a href="https://www.twitch.tv/jesslynnrose">Jess' Twitch channel</a> and <a href="https://www.twitch.tv/hola_soy_milk_">Carmen's Twitch channel</a>. You can also sign up for our weekly newsletter for lesson notes, show and tell projects, extra resources, and more.</p>
<p>Hope to see you soon!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ LeetCode Meditations: A Visual Handbook of Data Structures and Algorithms Concepts ]]>
                </title>
                <description>
                    <![CDATA[ It may seem like an oxymoron when the words "LeetCode" and "meditation" are used together – after all, one thing that almost everyone can agree is that LeetCode is challenging. It's called grinding LeetCode for a reason. It doesn't have anything to d... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/leetcode-dsa-concepts-handbook/</link>
                <guid isPermaLink="false">6838baed0c839b0170a2e731</guid>
                
                    <category>
                        <![CDATA[ Computer Science ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MathJax ]]>
                    </category>
                
                    <category>
                        <![CDATA[ leetcode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ DSA ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Eda Eren ]]>
                </dc:creator>
                <pubDate>Thu, 29 May 2025 19:52:13 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748548297673/2ea8ee5a-e873-4401-b024-86412bf00f8a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>It may seem like an oxymoron when the words "LeetCode" and "meditation" are used together – after all, one thing that almost everyone can agree is that <a target="_blank" href="https://leetcode.com/">LeetCode</a> is challenging. It's called <em>grinding</em> LeetCode for a reason.</p>
<p>It doesn't have anything to do with the platform, of course, but rather what it represents: tackling problems for hours on end, usually to find a solution that is even harder to understand.</p>
<p>But what is more challenging is finding a roadmap to solve those problems with very little knowledge of data structures and algorithms. This handbook is more or less based on the <a target="_blank" href="https://neetcode.io/practice?tab=blind75">Blind 75 list</a> that's included in <a target="_blank" href="http://neetcode.io">neetcode.io</a>'s practice problems. This is an amazing resource that offers an organized study roadmap for solving LeetCode problems.</p>
<p>In fact, why not take a more structured and <em>calmer</em> approach? We can treat learning about the topics on the list like taking a brief walk in nature – a sort of meditation, if you will.</p>
<p>That said, this handbook is not about specific problems. Rather it’s about understanding the concepts behind them in a casual manner. It is also language agnostic – sometimes you’ll see TypeScript, sometimes Python, and sometimes JavaScript.</p>
<p>This handbook also requires you to be patient, to relax, to take a step back and pay attention. The mid-quality GIFs used in the handbook (maybe ironically!) intend to encourage this. They are not videos, so you can wait for it to come to a moment that you didn't understand or missed instead of hastily rewinding it back or rushing to a certain point in the future.</p>
<p>Solving hundreds of LeetCode problems may be the gate to go through to get an interview at big tech companies…but learning the topics that the problems are about is not under anyone's monopoly.</p>
<p>With that said, let's start the first chapter.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-one-arrays-amp-hashing">Chapter One: Arrays &amp; Hashing</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-dynamic-arrays">Dynamic Arrays</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hash-tables">Hash Tables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prefix-sums">Prefix Sums</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-two-two-pointers">Chapter Two: Two Pointers</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-palindrome-example">Palindrome example</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-squares-of-a-sorted-array-example">Squares of a sorted array example</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-three-sliding-window">Chapter Three: Sliding Window</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-fixed-window-size">Fixed window size</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-dynamic-window-size">Dynamic window size</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-four-stack">Chapter Four: Stack</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-five-binary-search">Chapter Five: Binary Search</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-six-linked-lists">Chapter Six: Linked Lists</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-singly-linked-lists">Singly linked lists</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-doubly-linked-lists">Doubly linked lists</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-circular-linked-lists">Circular linked lists</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-interlude-fast-amp-slow-pointers">Interlude: Fast &amp; Slow Pointers</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-finding-the-middle-node-of-a-linked-list">Finding the middle node of a linked list</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-seven-trees">Chapter Seven: Trees</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-binary-trees-binary-search-trees-bsts">Binary trees, binary search trees (BSTs)</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-inserting-into-a-binary-search-tree">Inserting into a binary search tree</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-recursive-solution">Recursive solution</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-iterative-solution">Iterative solution</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-deleting-from-a-binary-search-tree">Deleting from a binary search tree</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-traversals">Traversals</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-depth-first-search-dfs">Depth-First Search (DFS)</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-preorder-traversal">Preorder traversal</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-inorder-traversal">Inorder traversal</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-postorder-traversal">Postorder traversal</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-breadth-first-search-bfs">Breadth-First Search (BFS)</a></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-eight-heap-priority-queue">Chapter Eight: Heap / Priority Queue</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-heap-properties">Heap properties</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-heaps-with-arrays">Heaps with arrays</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-insertingremoving-elements">Inserting/removing elements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-heapsort">Heapsort</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-nine-backtracking">Chapter Nine: Backtracking</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-subsets">Subsets</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-ten-tries">Chapter Ten: Tries</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-eleven-graphs">Chapter Eleven: Graphs</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-representing-graphs">Representing graphs</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-edge-list">Edge List</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adjacency-matrix">Adjacency Matrix</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adjacency-list">Adjacency List</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-traversals-1">Traversals</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-breadth-first-search">Breadth-First Search</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-depth-first-search">Depth-First Search</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-twelve-dynamic-programming">Chapter Twelve: Dynamic Programming</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-thirteen-intervals">Chapter Thirteen: Intervals</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-fourteen-bit-manipulation">Chapter Fourteen: Bit Manipulation</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-bitwise-operators">Bitwise operators</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-and-amp">AND (<code>&amp;</code>)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-or">OR (<code>|</code>)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-xor">XOR (<code>^</code>)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-not">NOT (<code>~</code>)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-left-shift-zero-fill-ltlt">Left shift (zero fill) (<code>&lt;&lt;</code>)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-right-shift-sign-preserving-gtgt">Right shift (sign preserving) (<code>&gt;&gt;</code>)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-right-shift-unsigned-gtgtgt">Right shift (unsigned) (<code>&gt;&gt;&gt;</code>)</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-getting-a-bit">Getting a bit</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-a-bit">Setting a bit</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-resources-amp-credits">Resources &amp; Credits</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before diving in, some familiarity with TypeScript/JavaScript and Python may be helpful, as these are the languages I use for the examples. Also, a basic understanding of Big O notation is useful as we go over time and space complexities.</p>
<p>Even though we don't go through the mathematics behind the concepts, some basic mathematical knowledge can also help. That said, it's definitely not necessary to enjoy or learn something useful from this handbook.</p>
<h2 id="heading-chapter-one-arrays-amp-hashing">Chapter One: Arrays &amp; Hashing</h2>
<p>Let's very briefly get to know our topics for this chapter: dynamic arrays, hash tables, and prefix sums.</p>
<h3 id="heading-dynamic-arrays">Dynamic Arrays</h3>
<p>Dynamic arrays are, well, dynamic. They're flexible and can change their size during execution.</p>
<p>Python's <code>list</code> type is a dynamic array. We can create an <code>items</code> list, for example:</p>
<pre><code class="lang-python">items = [<span class="hljs-number">3</span>, <span class="hljs-number">5</span>]
</code></pre>
<p>The <strong>length</strong> of <code>items</code> is 2, as you can see, but its <strong>capacity</strong> is greater than or equal to its length. In fact, capacity refers to the total size, whereas length is the actual size.</p>
<p>Since dynamic arrays are still arrays, they need a contiguous block of memory.</p>
<p>We can easily add an item to <code>items</code>:</p>
<pre><code class="lang-python">items.append(<span class="hljs-number">7</span>)
</code></pre>
<p>And add some more:</p>
<pre><code class="lang-python">items.append(<span class="hljs-number">9</span>)
items.append(<span class="hljs-number">11</span>)
items.append(<span class="hljs-number">13</span>)
</code></pre>
<p>All the while, the length and capacity of <code>items</code> keep growing dynamically.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747912308935/960ff442-e095-4781-8ab5-9b84e0ecb804.gif" alt="Animated visualization of four boxes for an &quot;items&quot; array that holds the values 3 and 5 on initialization, appending 7 to the array adds four more boxes to it." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<h4 id="heading-time-and-space-complexity">Time and space complexity</h4>
<p>Accessing an element is \(O(1)\) as we have <a target="_blank" href="https://en.wikipedia.org/wiki/Random_access">random access</a>.</p>
<p>Inserting a new element or deleting an element is \(O(n)\) (think about having to shift all the elements before inserting or after deleting an item). But, in order to not be too pessimistic, we can look at <a target="_blank" href="https://en.wikipedia.org/wiki/Amortized_analysis">amortized analysis</a> – in that case, inserting/deleting at the end of the array becomes \(O(1)\).</p>
<p>Space complexity is \(O(n)\), as the need for space will grow proportionately as the input increases.</p>
<p>If you need more info about time and space complexity, you can <a target="_blank" href="https://www.freecodecamp.org/news/big-o-notation-why-it-matters-and-why-it-doesnt-1674cfa8a23c/">refer to this guide</a>.</p>
<h3 id="heading-hash-tables">Hash Tables</h3>
<p>A hash table maps keys to values, implementing an <em>associative array</em>.</p>
<p>Python's <code>dict</code> is one example:</p>
<pre><code class="lang-python">number_of_petals = {
    <span class="hljs-string">'Euphorbia'</span>: <span class="hljs-number">2</span>, 
    <span class="hljs-string">'Trillium'</span>: <span class="hljs-number">3</span>, 
    <span class="hljs-string">'Columbine'</span>: <span class="hljs-number">5</span>,
}
</code></pre>
<p>Also JavaScript's "object"s:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> numberOfMoons = {
  <span class="hljs-string">'Earth'</span>: <span class="hljs-number">1</span>,
  <span class="hljs-string">'Mars'</span>: <span class="hljs-number">2</span>,
  <span class="hljs-string">'Jupiter'</span>: <span class="hljs-number">95</span>,
  <span class="hljs-string">'Saturn'</span>: <span class="hljs-number">146</span>,
  <span class="hljs-string">'Uranus'</span>: <span class="hljs-number">27</span>,
  <span class="hljs-string">'Neptune'</span>: <span class="hljs-number">14</span>,
};
</code></pre>
<p>There are two important ingredients for a hash table:</p>
<ul>
<li><p>an array of "buckets" to store the data</p>
</li>
<li><p>a hash function to map the data to a specific index in the array</p>
</li>
</ul>
<p>Hashes are usually large integers, so to find an index, we can take the result of the hash modulo the array's length.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747913143164/2a8371ba-133c-4d5a-a270-b44f935fc91b.gif" alt="Animated visualization of an array with 5 buckets, the hash function finding a bucket for each value in number_of_petals dict." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p><strong>Note:</strong> The <strong>hash function</strong> that's mapping the elements to buckets is <strong>not</strong> the <code>hash()</code> used in the visual (it's just a <a target="_blank" href="https://docs.python.org/3/library/functions.html#hash">Python function</a> to calculate the hash value of an object). The hash function in this case is the modulo ( <code>%</code> ) operation.</p>
<p>Here, with the hash value of each item's key, we calculate the remainder when it's divided by the length of the array to find which "bucket" it should go to.</p>
<p>The ratio of the number of elements to the number of buckets is called the <strong>load factor</strong>, and the higher it gets, the more <strong>collisions</strong> (when elements have to be inserted at the same place in the array) occur.</p>
<p>There are some collusion resolution tactics like <strong>linear probing</strong> (probing through the array until finding an empty bucket) and <strong>chaining</strong> (chaining multiple elements as linked lists), but we'll not go into those for now.</p>
<h4 id="heading-time-and-space-complexity-1">Time and space complexity</h4>
<p>The average case for searching, inserting, and deleting operations are \(O(1)\) as we use keys to look up the values.</p>
<p>Space complexity is \(O(n)\) as it grows linearly with the amount of elements.</p>
<h3 id="heading-prefix-sums">Prefix Sums</h3>
<p>A prefix sum is the sequence of numbers we get after adding the sums of running totals of another sequence. It's also called the <strong>cumulative sum</strong>.</p>
<p>The first element of the resulting array is the first element of the input array. That's fine. We start at the second item, and add the previous numbers each time as we go. That is:</p>
<p>$$result[i] = \begin{cases} nums[0] &amp; \text{if } i \text{ is zero} \\ result[i - 1] + nums[i] &amp; \text{if } i \geq 1 \end{cases}$$</p><p>In code, we can implement that easily:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">runningSum</span>(<span class="hljs-params">nums</span>):</span>
    result = [nums[<span class="hljs-number">0</span>]]

    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span>, len(nums)):
        result.append(result[i - <span class="hljs-number">1</span>] + nums[i])

    <span class="hljs-keyword">return</span> result
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747913501237/e9c95eef-6310-457c-b94e-da5a61fc890a.gif" alt="Animated visualization of runningSum of the array [1, 2, 3, 4, 5]." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<h4 id="heading-time-and-space-complexity-2">Time and space complexity</h4>
<p>Time complexity for a prefix sum is \(O(n)\) because we're iterating over each of the elements in the array.</p>
<p>The space complexity is also \(O(n)\) because the need for external space grows as the length of the original array grows.</p>
<h2 id="heading-chapter-two-two-pointers">Chapter Two: Two Pointers</h2>
<p>One of the techniques of iterating through an array is the <strong>two pointers technique</strong>, and it is as simple as it sounds: we just keep two pointers, one starting from the left, and the other from the right, gradually getting closer to each other.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747913831967/b8e251a9-2e9e-41be-84ed-46f2559b1515.gif" alt="Animated visualization of two pointers technique" class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<h3 id="heading-palindrome-example">Palindrome example</h3>
<p>A very basic example can be the one where we check if a string is a palindrome or not. A palindrome is a string that reads the same forwards and backwards.</p>
<p>In an imaginary world where all the inputs always consist of lowercase English letters, we can do it like this:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// s consists of lowercase English letters</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isPalindrome</span>(<span class="hljs-params">s: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">let</span> left = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">let</span> right = s.length - <span class="hljs-number">1</span>;

  <span class="hljs-keyword">while</span> (left &lt;= right) {
    <span class="hljs-keyword">if</span> (s[left++] !== s[right--]) {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
  }

  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
</code></pre>
<p>We initialize two pointers: <code>left</code> and <code>right</code>. <code>left</code> points to the start of the array, while the <code>right</code> points to the last element. As we loop while <code>left</code> is less than <code>right</code>, we check if they are equal. If not, we return <code>false</code> immediately. Otherwise, our <code>left</code> pointer is increased – that is, it's moved to the <em>right</em> one step, and our <code>right</code> pointer is decreased, meaning that it's moved to the <em>left</em> one step. When they eventually overlap, the loop terminates, and we return <code>true</code>.</p>
<p>Let's say our string is <code>'racecar'</code>, which is a palindrome. It will go like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747913932303/e60727a0-4e45-4452-8648-d35d1ae680c9.gif" alt="Animated visualization of isPalindrome, with the example 'racecar' resulting in true." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<h3 id="heading-squares-of-a-sorted-array-example">Squares of a sorted array example</h3>
<p>Another example where we can use the two pointers technique is the problem <a target="_blank" href="https://leetcode.com/problems/squares-of-a-sorted-array">Squares of a Sorted Array</a>.</p>
<p>The description says:</p>
<blockquote>
<p>Given an integer array <code>nums</code> sorted in <strong>non-decreasing</strong> order, return <em>an array of</em> <strong><em>the squares of each number</em></strong> <em>sorted in non-decreasing order</em>.</p>
</blockquote>
<p>For example, if the input is <code>[-4, -1, 0, 3, 10]</code>, the output should be <code>[0, 1, 9, 16, 100]</code>.</p>
<p>Now obviously, we can just square each one, and then sort the array with a built-in sort method, and be done with it. But a sorting operation is never better than \(O(n \ log \ n)\) runtime, so we can do it using two pointers in just \(O(n)\) time:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sortedSquares</span>(<span class="hljs-params">nums: <span class="hljs-built_in">number</span>[]</span>): <span class="hljs-title">number</span>[] </span>{
  <span class="hljs-keyword">let</span> left = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">let</span> right = nums.length - <span class="hljs-number">1</span>;
  <span class="hljs-keyword">let</span> result = [];

  <span class="hljs-keyword">while</span> (left &lt;= right) {
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Math</span>.abs(nums[left]) &gt; <span class="hljs-built_in">Math</span>.abs(nums[right])) {
      result.push(nums[left++] ** <span class="hljs-number">2</span>);
    } <span class="hljs-keyword">else</span> {
      result.push(nums[right--] ** <span class="hljs-number">2</span>);
    }
  }

  <span class="hljs-keyword">return</span> result.reverse();
}
</code></pre>
<p>We compare the absolute value of the items that <code>left</code> and <code>right</code> are pointing to, and push the square of the greater one to our <code>result</code> array. And we return the reversed version of it.</p>
<p><strong>Note:</strong> The reason we return the reversed result is that the array is initially already sorted, and we get the largest absolute value first. The reason that works is related to how <em>two pointers</em> work: as we start from both ends, we initially start with the smallest and largest values in the array.</p>
<p>Because we only make one pass through the array while comparing, and then later reversing, it ends up being \(O(n)\), a better runtime than \(O(n \ log \ n)\).</p>
<h2 id="heading-chapter-three-sliding-window">Chapter Three: Sliding Window</h2>
<p>Now that we're familiar with the Two Pointers technique, we can add another one to our toolbox: the Sliding Window. It's usually used for operations done on the subsets of a given data. It also comes in two flavors: <strong>fixed window size</strong> and <strong>dynamic window size</strong>.</p>
<h3 id="heading-fixed-window-size">Fixed window size</h3>
<p>If we have a size constraint in a given problem – say, we need to check a \(k\)-sized subarray – sliding window is an appropriate technique to use.</p>
<p>For example, getting the maximum subarray (of size \(k\)) sum of a given array can be done like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747914357907/ecd51e70-e649-4856-a563-47621b950526.gif" alt="Animated visualization of fixed window size sliding window technique, array [1, 5, 4, 2, 9] with k = 3, having the maxSum of 15." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>Note that the window size is \(k\), and it doesn't change throughout the operation – hence, <strong>fixed size</strong>.</p>
<p>A very cool thing to notice here is that with each <strong>slide</strong>, what happens to our sum is that we <em>add</em> the right element, and <em>decrease</em> the left element.</p>
<p>Let's look at an example for getting the maximum sum of subarray with given size <code>k</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">maxSubarray</span>(<span class="hljs-params">numbers: <span class="hljs-built_in">number</span>[], k: <span class="hljs-built_in">number</span></span>) </span>{
  <span class="hljs-keyword">if</span> (numbers.length &lt; k) {
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
  }

  <span class="hljs-keyword">let</span> currentSum = <span class="hljs-number">0</span>;

  <span class="hljs-comment">// Initial sum of the first window </span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; k; i++) {
    currentSum += numbers[i];
  }

  <span class="hljs-keyword">let</span> maxSum = currentSum;

  <span class="hljs-keyword">let</span> left = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">let</span> right = k;

  <span class="hljs-keyword">while</span> (right &lt; numbers.length) {
    currentSum = currentSum - numbers[left++] + numbers[right++];
    maxSum = <span class="hljs-built_in">Math</span>.max(maxSum, currentSum);
  }

  <span class="hljs-keyword">return</span> maxSum;
}
</code></pre>
<p><strong>Note:</strong> Updating the pointers can be done outside the brackets as well, like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">while</span> (right &lt; numbers.length) {
  currentSum = currentSum - numbers[left] + numbers[right];
  maxSum = <span class="hljs-built_in">Math</span>.max(maxSum, currentSum);
  left++;
  right++;
}
</code></pre>
<p>Since the postfix operator returns the value first, they can be used inside the brackets to be slightly more concise.</p>
<p>Here, we first get the initial sum of our window using the <code>for</code> loop, and set it as the maximum sum.</p>
<p>Then we initialize two pointers: <code>left</code> that points to the left end of the window, and <code>right</code> that points to the right end of the window. As we loop, we update our <code>currentSum</code>, decreasing the <code>left</code> value, and adding the <code>right</code> value. When our current sum is more than the maximum sum, <code>maxSum</code> variable is updated as well.</p>
<h3 id="heading-dynamic-window-size">Dynamic window size</h3>
<p>As opposed to the fixed window size version, the size of the window changes dynamically this time.</p>
<p>For example, let's take a brief look at the problem <a target="_blank" href="https://leetcode.com/problems/best-time-to-buy-and-sell-stock">Best Time to Buy and Sell Stock</a>. We need to choose a day to buy a stock, and sell it in the <em>future</em>. The numbers in the array are prices, and we need to buy the stock at as low a price as we can, and sell it as high as we can.</p>
<p>We can initialize <code>left</code> and <code>right</code> pointers again, but this time, we'll update them depending on a condition. When the left item is less than the one on the right, that means it's good – we can buy and sell at those prices, so we get their difference and update our <code>maxDiff</code> variable that holds the maximum difference between the two.</p>
<p>If, however, the left one is greater than the right one, we update our <code>left</code> pointer to be where the <code>right</code> is at. In both cases, we'll continue updating <code>right</code> until we reach the end of the array.</p>
<p>With the blue arrow indicating the left pointer, and the red the right one, the process looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747914550588/222996a0-d2a6-414e-86cf-fe60deb908d8.gif" alt="Animated visualization of dynamic window size sliding window technique, array [7, 1, 5, 3, 6] having the maxDiff of 5." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>The solution looks like this:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">maxProfit</span>(<span class="hljs-params">prices: <span class="hljs-built_in">number</span>[]</span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">let</span> left = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">let</span> right = left + <span class="hljs-number">1</span>;
  <span class="hljs-keyword">let</span> maxDiff = <span class="hljs-number">0</span>;

  <span class="hljs-keyword">while</span> (right &lt; prices.length) {
    <span class="hljs-keyword">if</span> (prices[left] &lt; prices[right]) {
      <span class="hljs-keyword">let</span> diff = prices[right] - prices[left];
      maxDiff = <span class="hljs-built_in">Math</span>.max(maxDiff, diff);
    } <span class="hljs-keyword">else</span> {
      left = right;
    }

    right++;
  }

  <span class="hljs-keyword">return</span> maxDiff;
}
</code></pre>
<p><strong>Note:</strong> This one is also called <strong>fast/catch-up</strong> version of dynamic sliding window, because the <code>left</code> pointer jumps to catch up with the <code>right</code> pointer in the <code>else</code> block.</p>
<h4 id="heading-time-and-space-complexity-3">Time and space complexity</h4>
<p>Both examples have the same time and space complexity: The time complexity is \(O(n)\) because in the worst case we iterate through all the elements in the array. The space complexity is \(O(1)\) as we don't need additional space.</p>
<h2 id="heading-chapter-four-stack">Chapter Four: Stack</h2>
<p>A stack data type is perhaps one of the most well-known ones. A stack of books might be a good example to visualize, but insertion and deletion can only happen from the one end. A stack operates through the last-in first-out (LIFO) principle: the last item to go in is the first to go out.</p>
<p>Usually we'll have methods for <em>pushing</em> an element onto the stack, and <em>popping</em> an element from the stack.</p>
<p>For example, let's say we're looking for valid parentheses in a given string, and the operation we'll do goes like this.</p>
<p>As we iterate over the characters in the string, we <em>push</em> the character onto the stack. If we pushed a closing parenthesis (one of <code>)</code>, <code>}</code>, or <code>]</code>), then, if the previous pushed element is its opening pair, we'll <em>pop</em> that pair from the stack.</p>
<p>If, at the end, the stack is empty, the string consists of valid parentheses.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747914829489/e86baf72-22f6-41fe-9de9-f4da136a8777.gif" alt="Animated visualization of pushing to and popping off from a stack of parentheses." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>A stack can be implemented as an array or a linked list. But using linked lists is more common because with arrays, we have a potential <em>stack overflow</em> when we predefine a maximum stack size. On the other hand, linked lists are not static when it comes to memory, so they are a good candidate to implement stacks.</p>
<p>Linked lists are also efficient because we are using one end of the stack for insertion and deletion, and doing these are constant time operations.</p>
<p>Let's look at one easy stack implementation in Python.</p>
<p>Now, we can use a <code>list</code>, but <a target="_blank" href="https://docs.python.org/3/faq/design.html#how-are-lists-implemented-in-cpython">a list in Python is implemented as a dynamic array underneath</a>, so at one point, pushing an item can be an \(O(n)\) operation if the list needs to be copied into another memory location. For that reason, we'll use a <a target="_blank" href="https://docs.python.org/3/library/collections.html#collections.deque"><code>deque</code></a>, which is implemented as a doubly-linked list, so that we know push and pop operations will be \(O(1)\).</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> deque

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Stack</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self._stack = deque()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">push</span>(<span class="hljs-params">self, item</span>):</span>
        self._stack.append(item)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pop</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self._stack.pop()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">peek</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self._stack[<span class="hljs-number">-1</span>]

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_empty</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">not</span> bool(len(self._stack))

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">size</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> len(self._stack)
</code></pre>
<p>In addition to <code>push</code> and <code>pop</code>, we'll also usually have functions like <code>peek</code> to get the topmost item in the stack, <code>is_empty</code> to check if the stack is empty, and <code>size</code> to get the size of the stack.</p>
<p>We can also do it using JavaScript. Now, we can do it using an array, but we want to use a linked list instead. Since we don't have a robust built-in library like Python this time, we'll implement a very simple version of it ourselves. Even though we haven't seen linked lists so far, the basic idea is that we have nodes, each of which has a <code>data</code> value, and a <code>next</code> pointer pointing to the next node.</p>
<p>Let's create a simple node first:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node</span> </span>{
  <span class="hljs-keyword">constructor</span>(data) {
    <span class="hljs-built_in">this</span>.data = data;
    <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
  }
}
</code></pre>
<p>We can write our stack now:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Stack</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.top = <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">this</span>.length = <span class="hljs-number">0</span>;
  }

  push(item) {
    <span class="hljs-keyword">const</span> node = <span class="hljs-keyword">new</span> Node(item);
    node.next = <span class="hljs-built_in">this</span>.top;
    <span class="hljs-built_in">this</span>.top = node;
    <span class="hljs-built_in">this</span>.length++;
  }

  pop() {
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.isEmpty()) { <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; }

    <span class="hljs-keyword">const</span> data = <span class="hljs-built_in">this</span>.top.data;
    <span class="hljs-built_in">this</span>.top = <span class="hljs-built_in">this</span>.top.next;
    <span class="hljs-built_in">this</span>.length--;

    <span class="hljs-keyword">return</span> data;
  }

  peek() {
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.isEmpty()) { <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; }

    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.top.data;
  }

  isEmpty() {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.size() === <span class="hljs-number">0</span>;
  }

  size() {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.length;
  }
}
</code></pre>
<p>Now, let’s use it:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> myStack = <span class="hljs-keyword">new</span> Stack();

myStack.push(<span class="hljs-number">5</span>);
myStack.push(<span class="hljs-number">17</span>);
myStack.push(<span class="hljs-number">55345</span>);
myStack.push(<span class="hljs-number">0</span>);
myStack.push(<span class="hljs-number">103</span>)

<span class="hljs-built_in">console</span>.log(myStack.size()) <span class="hljs-comment">// 5</span>
<span class="hljs-built_in">console</span>.log(myStack.peek()) <span class="hljs-comment">// 103</span>

myStack.pop()

<span class="hljs-built_in">console</span>.log(myStack.size()) <span class="hljs-comment">// 4</span>
<span class="hljs-built_in">console</span>.log(myStack.peek()) <span class="hljs-comment">// 0</span>
</code></pre>
<h4 id="heading-time-and-space-complexity-4">Time and space complexity</h4>
<p>Each method we defined for our stack has \(O(1)\) time complexity, and it would be the same if we were to use an array as well. However, as mentioned above, arrays have limitations in that having to allocate a predefined stack size can lead to a stack overflow. And if we were to use a dynamic array, the whole array might need to be copied to go into another memory location after a certain size is reached, leading to \(O(n)\) time. So, linked lists are ideal to implement a stack data type.</p>
<p>If the space complexity is linear – \(O(n)\)– the stack will grow linearly with the number of items in it.</p>
<h2 id="heading-chapter-five-binary-search">Chapter Five: Binary Search</h2>
<p>Binary search is one of the most well-known algorithms. It's also a <a target="_blank" href="https://brilliant.org/wiki/divide-and-conquer/">divide-and-conquer algorithm</a>, where we break the problem into smaller components.</p>
<p>The crux of binary search is to find a target element in a given sorted array. We have two pointers: <code>high</code> to point to the largest element, and <code>low</code> to point to the smallest element. We first initialize them for the whole array itself, with <code>high</code> being the last index and <code>low</code> being the first index.</p>
<p>Then, we calculate the midpoint. If the target is greater than the midpoint, then we adjust our <code>low</code> pointer to point to the <code>mid + 1</code>, otherwise if the target is less than the midpoint, we adjust <code>high</code> to be <code>mid - 1</code>. With each iteration, we eliminate half the array until the midpoint equals the target or the <code>low</code> pointer passes <code>high</code>.</p>
<p>If we find the index of the target, we can return it as soon as we find it. Otherwise, we can just return <code>-1</code> to indicate that the target doesn't exist in the array.</p>
<p>For example, if we have a <code>nums</code> array <code>[-1, 0, 3, 5, 9, 12]</code> and our <code>target</code> is <code>9</code>, the operation looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747915126875/c27134cb-ae7c-4f09-88d8-13fc64900319.gif" alt="Animated visualization of binary search, array [-1, 0, 3, 5, 9. 12] with target = 9, the result being the index 4." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>We can write it in TypeScript like this:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">search</span>(<span class="hljs-params">nums: <span class="hljs-built_in">number</span>[], target: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">let</span> high = nums.length - <span class="hljs-number">1</span>;
  <span class="hljs-keyword">let</span> low = <span class="hljs-number">0</span>;

  <span class="hljs-keyword">while</span> (high &gt;= low) {
    <span class="hljs-keyword">let</span> mid = <span class="hljs-built_in">Math</span>.floor((high + low) / <span class="hljs-number">2</span>);

    <span class="hljs-keyword">if</span> (target &gt; nums[mid]) {
      low = mid + <span class="hljs-number">1</span>;
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (target &lt; nums[mid]) {
      high = mid - <span class="hljs-number">1</span>;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">return</span> mid;
    }
  }

  <span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
}
</code></pre>
<h4 id="heading-time-and-space-complexity-5">Time and space complexity</h4>
<p>The time complexity of a binary search algorithm is \(O(log \ n)\) in the worst case. (For example, if the target is not in the array, we'll be halving the array until there is one element left.) The space complexity is \(O(1)\) as we don't need extra space.</p>
<h2 id="heading-chapter-six-linked-lists">Chapter Six: Linked Lists</h2>
<p>A linked list is a linear data structure that you're likely to be familiar with. It is also a data structure that can grow and shrink dynamically – so unlike arrays, there's no need to allocate memory beforehand.</p>
<p>An important part of a linked list is the <strong>head pointer</strong> that points to the beginning of the list. There may or may not be a <strong>tail pointer</strong> that also points to the end of the list.</p>
<p>The core ingredient of a linked list is a simple node, which consists of two parts: data and the next pointer. So, it is an important idea to remember: <em>a node only knows about its data and its neighbor.</em></p>
<p>The very last node in the linked list points to <code>null</code> to indicate it's the end of the list.</p>
<p>But there are different types of linked lists that differ from each other slightly, so let's briefly take a look at them.</p>
<h3 id="heading-singly-linked-lists">Singly linked lists</h3>
<p>The core idea with singly linked lists is that each node, along with the data it has, has a pointer that points <em>only</em> to the next node:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node</span> </span>{
  <span class="hljs-keyword">constructor</span>(data) {
    <span class="hljs-built_in">this</span>.data = data;
    <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
  }
}
</code></pre>
<p>And here is an example where we have three nodes, holding the values <code>1</code>, <code>2</code>, and <code>3</code> consecutively:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747915365474/8604a69f-f24f-4dc5-b4a0-2eba452a4305.gif" alt="Animated visualization of a singly linked list, with nodes having 1, 2, and 3 as values." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>Here is a simple implementation of a singly linked list in JavaScript:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SinglyLinkedList</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">this</span>.length = <span class="hljs-number">0</span>;
  }

  <span class="hljs-comment">// Add value to the end of the list</span>
  append(value) {
    <span class="hljs-keyword">let</span> node = <span class="hljs-keyword">new</span> Node(value);
    <span class="hljs-comment">// If the list is empty</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) {
      <span class="hljs-built_in">this</span>.head = node;
      <span class="hljs-built_in">this</span>.tail = <span class="hljs-built_in">this</span>.head;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-built_in">this</span>.tail.next = node;
      <span class="hljs-built_in">this</span>.tail = node;
    }

    <span class="hljs-built_in">this</span>.length++;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;
  }

  <span class="hljs-comment">// Add value to the beginning of the list</span>
  prepend(value) {
    <span class="hljs-keyword">let</span> node = <span class="hljs-keyword">new</span> Node(value);
    <span class="hljs-comment">// If the list is empty</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) {
      <span class="hljs-built_in">this</span>.head = node;
      <span class="hljs-built_in">this</span>.tail = <span class="hljs-built_in">this</span>.head;
    } <span class="hljs-keyword">else</span> {
      node.next = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-built_in">this</span>.head = node;
    }

    <span class="hljs-built_in">this</span>.length++;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;
  }

  remove(value) {
    <span class="hljs-comment">// If the list is empty, return null</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) { 
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; 
    }

    <span class="hljs-comment">// If it is the first element</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.data === value) {
      <span class="hljs-built_in">this</span>.head = <span class="hljs-built_in">this</span>.head.next;
      <span class="hljs-built_in">this</span>.length--;
      <span class="hljs-comment">// If it is the only element </span>
      <span class="hljs-comment">// (we don't have anything after removing it)</span>
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) {
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
      } 
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.head;

    <span class="hljs-keyword">while</span> (currentNode.next) {
      <span class="hljs-keyword">if</span> (currentNode.next.data === value) {
        currentNode.next = currentNode.next.next;
        <span class="hljs-comment">// If it is the last element, update tail</span>
        <span class="hljs-keyword">if</span> (currentNode.next === <span class="hljs-literal">null</span>) {
          <span class="hljs-built_in">this</span>.tail = currentNode;
        } 
        <span class="hljs-built_in">this</span>.length--;
        <span class="hljs-keyword">return</span>;
      }
      currentNode = currentNode.next;
    }
  }

  search(value) {
    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.head;

    <span class="hljs-keyword">while</span> (currentNode) {
      <span class="hljs-keyword">if</span> (currentNode.data === value) {
        <span class="hljs-keyword">return</span> currentNode;
      }
      currentNode = currentNode.next;
    }

    <span class="hljs-comment">// If the value does not exist, return null</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }

  printList() {
    <span class="hljs-keyword">let</span> values = [];
    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.head;
    <span class="hljs-keyword">while</span> (currentNode) {
      values.push(currentNode.data);
      currentNode = currentNode.next;
    }

    <span class="hljs-built_in">console</span>.log(values);
  }
}
</code></pre>
<p><strong>Note:</strong> We'll keep a tail pointer in all these examples for convenience. It <a target="_blank" href="https://softwareengineering.stackexchange.com/a/301863">doesn't hurt</a> to have a tail pointer.</p>
<p>We can now use it:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> mySinglyLinkedList = <span class="hljs-keyword">new</span> SinglyLinkedList();

mySinglyLinkedList.prepend(<span class="hljs-number">3</span>);
mySinglyLinkedList.prepend(<span class="hljs-number">143</span>);
mySinglyLinkedList.prepend(<span class="hljs-number">5</span>);

mySinglyLinkedList.printList(); <span class="hljs-comment">// [ 5, 143, 3 ]</span>

mySinglyLinkedList.append(<span class="hljs-number">21</span>);
mySinglyLinkedList.printList(); <span class="hljs-comment">// [ 5, 143, 3, 21 ]</span>

<span class="hljs-built_in">console</span>.log(mySinglyLinkedList.search(<span class="hljs-number">143</span>));
<span class="hljs-comment">// Node {</span>
<span class="hljs-comment">//   data: 143,</span>
<span class="hljs-comment">//   next: Node { data: 3, next: Node { data: 21, next: null } }</span>
<span class="hljs-comment">// }</span>

mySinglyLinkedList.remove(<span class="hljs-number">143</span>);
mySinglyLinkedList.printList(); <span class="hljs-comment">// [ 5, 3, 21 ]</span>

<span class="hljs-built_in">console</span>.log(mySinglyLinkedList.search(<span class="hljs-number">143</span>)); <span class="hljs-comment">// null</span>
</code></pre>
<h3 id="heading-doubly-linked-lists">Doubly linked lists</h3>
<p>Doubly linked lists differ from the "singly" ones in that each node also has another pointer that points to the previous element.</p>
<p>So, this time, a single node will look different:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node</span> </span>{
  <span class="hljs-keyword">constructor</span>(data) {
    <span class="hljs-built_in">this</span>.data = data;
    <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">this</span>.previous = <span class="hljs-literal">null</span>;
  }
}
</code></pre>
<p>Here is the same example as above, but as a doubly linked list:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747915463580/605d3541-a702-4bef-9777-28c47800b7aa.gif" alt="Animated visualization of a doubly linked list, with nodes having 1, 2 and 3 as values." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>A simple implementation might look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DoublyLinkedList</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">this</span>.length = <span class="hljs-number">0</span>;
  }

  <span class="hljs-comment">// Add value to the end of the list</span>
  append(value) {
    <span class="hljs-keyword">let</span> node = <span class="hljs-keyword">new</span> Node(value);
    <span class="hljs-comment">// If the list is empty</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) {
      <span class="hljs-built_in">this</span>.head = node;
      <span class="hljs-built_in">this</span>.tail = <span class="hljs-built_in">this</span>.head;
    } <span class="hljs-keyword">else</span> {
      node.previous = <span class="hljs-built_in">this</span>.tail;
      <span class="hljs-built_in">this</span>.tail.next = node;
      <span class="hljs-built_in">this</span>.tail = node;
    }

    <span class="hljs-built_in">this</span>.length++;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;
  }

  <span class="hljs-comment">// Add value to the beginning of the list</span>
  prepend(value) {
    <span class="hljs-keyword">let</span> node = <span class="hljs-keyword">new</span> Node(value);
    <span class="hljs-comment">// If the list is empty</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) {
      <span class="hljs-built_in">this</span>.head = node;
      <span class="hljs-built_in">this</span>.tail = <span class="hljs-built_in">this</span>.head;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-built_in">this</span>.head.previous = node;
      node.next = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-built_in">this</span>.head = node;
    }

    <span class="hljs-built_in">this</span>.length++;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;
  }

  remove(value) {
    <span class="hljs-comment">// If the list is empty, return null</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) { 
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }

    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.head;

    <span class="hljs-comment">// If it is the first element</span>
    <span class="hljs-keyword">if</span> (currentNode.data === value) {
      <span class="hljs-built_in">this</span>.head = currentNode.next;
      <span class="hljs-comment">// If the removed element is not the only one,</span>
      <span class="hljs-comment">// make the previous pointer of the new head null</span>
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head.previous = <span class="hljs-literal">null</span>;
      <span class="hljs-comment">// If the removed element was the only element,</span>
      <span class="hljs-comment">// point the tail to null as well</span>
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
      }
      <span class="hljs-built_in">this</span>.length--;
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">while</span> (currentNode) {
      <span class="hljs-keyword">if</span> (currentNode.data === value) {
        <span class="hljs-keyword">if</span> (currentNode.previous) {
          currentNode.previous.next = currentNode.next;
        }
        <span class="hljs-keyword">if</span> (currentNode.next) {
          currentNode.next.previous = currentNode.previous;
        <span class="hljs-comment">// If it's the last element in the list, update tail</span>
        <span class="hljs-comment">// to point to the previous node</span>
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">this</span>.tail = currentNode.previous;
        }

        <span class="hljs-built_in">this</span>.length--;
        <span class="hljs-keyword">return</span>;
      }

      currentNode = currentNode.next;
    }
  }

  search(value) {
    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.head;
    <span class="hljs-keyword">while</span> (currentNode) {
      <span class="hljs-keyword">if</span> (currentNode.data === value) {
        <span class="hljs-keyword">return</span> currentNode;
      }
      currentNode = currentNode.next;
    }

    <span class="hljs-comment">// If the value does not exist, return null</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }

  printList() {
    <span class="hljs-keyword">let</span> values = [];
    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.head;

    <span class="hljs-keyword">while</span> (currentNode) {
      values.push(currentNode.data);
      currentNode = currentNode.next;
    }

    <span class="hljs-built_in">console</span>.log(values);
  }
}
</code></pre>
<h3 id="heading-circular-linked-lists">Circular linked lists</h3>
<p>With circular linked lists, we have the last node also pointing to the first element, creating circularity.</p>
<p>We'll only look at the singly circular linked list for simplicity's sake, so our node will look the same as in the first example:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Node</span> </span>{
  <span class="hljs-keyword">constructor</span>(data) {
    <span class="hljs-built_in">this</span>.data = data;
    <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
  }
}
</code></pre>
<p>The same example, in a circular linked list fashion:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747915538225/ea4341cb-2a39-4728-b833-362455a51cdd.gif" alt="Animated visualization of a circular linked list, nodes having 1, 2, and 3 as values." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>Here is a simple implementation:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CircularLinkedList</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">this</span>.length = <span class="hljs-number">0</span>;
  }

  <span class="hljs-comment">// Add value to the "end" of the list</span>
  append(value) {
    <span class="hljs-keyword">let</span> node = <span class="hljs-keyword">new</span> Node(value);
    <span class="hljs-comment">// If the list is empty</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) {
      <span class="hljs-built_in">this</span>.head = node;
      <span class="hljs-built_in">this</span>.tail = node;
      <span class="hljs-comment">// As the only node in the list, it should point to itself</span>
      node.next = node;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// As the "last" node, it should point to the head (this.tail.next)</span>
      node.next = <span class="hljs-built_in">this</span>.tail.next;
      <span class="hljs-built_in">this</span>.tail.next = node;
      <span class="hljs-built_in">this</span>.tail = node;
    }

    <span class="hljs-built_in">this</span>.length++;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;
  }

  <span class="hljs-comment">// Add value to the beginning of the list</span>
  prepend(value) {
    <span class="hljs-keyword">let</span> node = <span class="hljs-keyword">new</span> Node(value);
    node.next = <span class="hljs-built_in">this</span>.head;
    <span class="hljs-comment">// Update last node's next pointer to point to the new node</span>
    <span class="hljs-built_in">this</span>.tail.next = node;
    <span class="hljs-built_in">this</span>.head = node;
    <span class="hljs-built_in">this</span>.length++;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>;
  }  

  remove(value) {
    <span class="hljs-comment">// If the list is empty, return null</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) { 
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>; 
    }

    <span class="hljs-comment">// If it is the first element</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.data === value) {
      <span class="hljs-comment">// If it's the only element</span>
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.next === <span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">return</span>;
      }
      <span class="hljs-built_in">this</span>.head = <span class="hljs-built_in">this</span>.head.next;
      <span class="hljs-built_in">this</span>.tail.next = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-built_in">this</span>.length--;
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.head;
    <span class="hljs-keyword">let</span> prevNode = <span class="hljs-literal">null</span>;

    <span class="hljs-comment">// Iterate until you find the value or</span>
    <span class="hljs-comment">// you don't find it after traversing the whole list</span>
    <span class="hljs-keyword">while</span> (currentNode.data !== value || prevNode === <span class="hljs-literal">null</span>) {
      <span class="hljs-keyword">if</span> (currentNode.next === <span class="hljs-built_in">this</span>.head) { 
        <span class="hljs-keyword">break</span>; 
      }
      prevNode = currentNode;
      currentNode = currentNode.next;
    }

    <span class="hljs-keyword">if</span> (currentNode.data === value) {
      <span class="hljs-comment">// If there is a previous node before the element to be removed,</span>
      <span class="hljs-comment">// update the previous node's next pointer to point to</span>
      <span class="hljs-comment">// the one after the element to be removed</span>
      <span class="hljs-comment">// (unlink it)</span>
      <span class="hljs-keyword">if</span> (prevNode) {
        prevNode.next = currentNode.next;
        <span class="hljs-comment">// If the element to be removed is the last one,</span>
        <span class="hljs-comment">// update tail to be the previous node</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.tail === currentNode) {
          <span class="hljs-built_in">this</span>.tail = prevNode;
        }
      <span class="hljs-comment">// If the element to be removed is the first one in the list</span>
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// If it's the only one in the list</span>
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.next === <span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
          <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-built_in">this</span>.head.next;
          <span class="hljs-built_in">this</span>.tail.next = <span class="hljs-built_in">this</span>.head;
        }
      }
    }
  }

  printList() {
    <span class="hljs-keyword">let</span> nodes = [];
    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.head;
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head === <span class="hljs-literal">null</span>) { 
      <span class="hljs-built_in">console</span>.log(nodes); 
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-comment">// Traverse the list once to add the values, </span>
    <span class="hljs-comment">// don't go in circles</span>
    <span class="hljs-keyword">do</span> {
      nodes.push(currentNode.data);
      currentNode = currentNode.next;
    } <span class="hljs-keyword">while</span> (currentNode !== <span class="hljs-built_in">this</span>.head);

    <span class="hljs-built_in">console</span>.log(nodes);
  }
}
</code></pre>
<h4 id="heading-time-and-space-complexity-6">Time and space complexity</h4>
<p>With linked lists, the time complexity for accessing an element is in the worst case \(O(n)\). <em>Prepending</em> and <em>appending</em> an element depends on whether we have a tail pointer. If we have it, then both operations are \(O(1)\), as we only need to arrange pointers. But if we don't have a tail pointer, <em>appending</em> an element requires traversing the whole list, so it is an \(O(n)\) operation. Removing an element is similar – in the worst case, it is \(O(n)\).</p>
<p>If the space complexity is linear – \(O(n)\)– then the amount of data to store grows linearly with the number of nodes in the list.</p>
<h2 id="heading-interlude-fast-amp-slow-pointers">Interlude: Fast &amp; Slow Pointers</h2>
<p>Let's take a quick look at a technique that comes in handy when it comes to working with linked lists.</p>
<p>We can keep two pointers while traversing a linked list: fast and slow. While the fast one increases by two steps, the slow pointer will increase by just one step.</p>
<h3 id="heading-finding-the-middle-node-of-a-linked-list">Finding the middle node of a linked list</h3>
<p>When the fast pointer reaches the end of the list, the slow pointer will be at the "middle" node.</p>
<p>Let's see how it might work:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> slow = head;
<span class="hljs-keyword">let</span> fast = head;

<span class="hljs-keyword">while</span> (fast !== <span class="hljs-literal">null</span> &amp;&amp; fast.next !== <span class="hljs-literal">null</span>) {
  slow = slow.next;
  fast = fast.next.next;
}
</code></pre>
<p>We can think of a list like <code>[1, 2, 3, 4, 5]</code> (where each value is a node in the linked list).</p>
<p>Both <code>fast</code> and <code>slow</code> start pointing to the head, that is, <code>1</code>.</p>
<p>Then, we update the slow pointer one step, which will be <code>2</code>. And, <code>fast</code> will be at <code>3</code>.</p>
<p>When we update <code>slow</code> again, it will be at <code>3</code>. When the fast pointer increases, it will be two steps ahead, and its <code>next</code> pointer will point to the <code>null</code> value, at which point our loop will stop iterating.</p>
<p><code>slow</code> will end up pointing to the node with the value <code>3</code>, which is the middle node.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747915833404/d3c562e8-36e3-459f-a29c-860e0d4869f0.gif" alt="Animated visualization of fast and slow pointers technique on a linked list of 5 nodes." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>With an even number of nodes, there are two candidates for the middle node. For example, with a list like <code>[1, 2, 3, 4]</code>, our current implementation will find the middle as <code>3</code>. This technique is also useful to detect cycles in a linked list.</p>
<h2 id="heading-chapter-seven-trees">Chapter Seven: Trees</h2>
<p>Let’s take a look at a non-linear data structure that is pretty familiar to many developers: trees.</p>
<p>Whether familiarity breeds contempt or not is arguable, so let's start with the simplest component of a tree: a node.</p>
<p>Trees, like linked lists, are made up of nodes. The simplest version of a tree is just the <strong>root node</strong> which doesn't have any edges (links) pointing to it; that is, it has no <strong>parent nodes</strong>. It is the starting point, in a way.</p>
<p>A tree can only have one root node, and when you think about it, <em>if there are</em> \(n\) <em>nodes in a tree, that means there are</em> \(n - 1\) <em>edges (links)</em> because there is no edge (link) pointing to the root node.</p>
<p>If you've looked at a tree long enough, you might've had a moment of epiphany: a tree has smaller trees within itself. A branch may as well be a trunk, having other branches for the little tree it constitutes.</p>
<p>The tree data structure is like this, it is recursive: <em>a child node can be the root of a subtree</em>.</p>
<p>Two terms that are important when it comes to a tree node are <strong>depth</strong> and <strong>height</strong>.</p>
<p>The <strong>depth</strong> of a node is how far away it is from the root node (how many edges (links) does it take to travel from the root node to it), and the <strong>height</strong> of a node is how far away it is from its furthest <strong>leaf node</strong> (which is a node that has no children).</p>
<p><strong>Note:</strong> The height of the root node is the same as the height of the whole tree.</p>
<p>A <strong>balanced tree</strong> is one where <em>the heights of the left and right subtrees of every node differ by at most 1</em>.</p>
<h3 id="heading-binary-trees-binary-search-trees-bsts">Binary trees, binary search trees (BSTs)</h3>
<p>A <strong>binary tree</strong> is a tree where each node has at most two children. That is, a node can have a left child node and a right child node, and no more.</p>
<p>The maximum number of nodes in a binary tree is \(2^h - 1\) where \(h\) is the height of the tree. This is where the <em>binary</em> of the binary tree makes sense: on each level, the number of nodes grows proportionately to the exponents of \(2\).</p>
<p>For example, the number of nodes on the first level (the 0th level) is \(2^0 = 1\), which is just the root node. The second level has at most 2 nodes: \(2^1 = 2\) (remember that we're counting from \(0\), so the second level is \(1\)).</p>
<p>A <strong>binary search tree</strong> is a binary tree where the values smaller than the node go to its left and those greater than it go to its right:</p>
<p>$$\text{left children } \lt \text{ node } \lt \text{ right children}$$</p><p>Here is an example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747916761231/c0b5301c-6832-43a9-8abd-30f578ac2a29.gif" alt="Animated visualization of a binary search tree with 8 as the root node, 3 as its left child, 10 as its right child. 3 has 1 as it left child, 6 as its right child. 6 has 4 as its left child, 7 as its right child. 10 has 14 as its right child. 14 has 13 as its left child. " class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>We can define a tree node like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> TreeNode {
  val: <span class="hljs-built_in">number</span>;
  left: TreeNode | <span class="hljs-literal">null</span>;
  right: TreeNode | <span class="hljs-literal">null</span>;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">val: <span class="hljs-built_in">number</span>, left?: TreeNode | <span class="hljs-literal">null</span>, right?: TreeNode | <span class="hljs-literal">null</span></span>) {
    <span class="hljs-built_in">this</span>.val = val;
    <span class="hljs-built_in">this</span>.left = (left === <span class="hljs-literal">undefined</span> ? <span class="hljs-literal">null</span> : left);
    <span class="hljs-built_in">this</span>.right = (right === <span class="hljs-literal">undefined</span> ? <span class="hljs-literal">null</span> : right);
  }
}
</code></pre>
<h4 id="heading-inserting-into-a-binary-search-tree">Inserting into a binary search tree</h4>
<p>If we want to insert a new node into a binary search tree, we need to insert it into its proper place to keep the properties of a BST intact.</p>
<h5 id="heading-recursive-solution">Recursive solution:</h5>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">insertIntoBST</span>(<span class="hljs-params">root: TreeNode | <span class="hljs-literal">null</span>, val: <span class="hljs-built_in">number</span></span>) </span>{
  <span class="hljs-keyword">if</span> (root === <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TreeNode(val);
  }

  <span class="hljs-keyword">if</span> (val &lt; root.val) {
    root.left = insertIntoBST(root.left, val);
  } <span class="hljs-keyword">else</span> {
    root.right = insertIntoBST(root.right, val);
  }

  <span class="hljs-keyword">return</span> root;
}
</code></pre>
<p>Here, we traverse the tree until we find a space (a <code>null</code> position) for our value that is waiting to be a <code>TreeNode</code>. We start with the root node. If the value of the node-to-be-inserted is less than the value of the root node, we go left (passing <code>root.left</code> as the <code>root</code> argument to the function). Otherwise, we go right (passing <code>root.right</code> as the <code>root</code> argument).</p>
<h4 id="heading-time-and-space-complexity-7">Time and space complexity</h4>
<p>The time complexity is \(O(h)\) where \(h\) is the height of the tree. On each level in the tree, we either go left or right, so we don't necessarily visit every single node. The space complexity is also \(O(h)\) because we use recursion, creating a new stack frame for each function call.</p>
<p><em>Note that if the tree is unbalanced, the time and space complexity can be said to be</em> \(O(n)\)<em>.</em></p>
<h5 id="heading-iterative-solution">Iterative solution:</h5>
<p>We can also do it iteratively, using pointers only:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">insertIntoBST</span>(<span class="hljs-params">root: TreeNode | <span class="hljs-literal">null</span>, val: <span class="hljs-built_in">number</span></span>) </span>{
  <span class="hljs-keyword">if</span> (root === <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> TreeNode(val);
  }

  <span class="hljs-keyword">let</span> prevNode: TreeNode | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
  <span class="hljs-keyword">let</span> currentNode: TreeNode | <span class="hljs-literal">null</span> = root;

  <span class="hljs-keyword">while</span> (currentNode !== <span class="hljs-literal">null</span>) {
    prevNode = currentNode;
    <span class="hljs-keyword">if</span> (val &lt; currentNode.val) {
      currentNode = currentNode.left;
    } <span class="hljs-keyword">else</span> {
      currentNode = currentNode.right;
    }
  }

  <span class="hljs-keyword">if</span> (prevNode) {
    <span class="hljs-keyword">if</span> (val &lt; prevNode.val) {
      prevNode.left = <span class="hljs-keyword">new</span> TreeNode(val);
    } <span class="hljs-keyword">else</span> {
      prevNode.right = <span class="hljs-keyword">new</span> TreeNode(val);
    }
  }

  <span class="hljs-keyword">return</span> root;
}
</code></pre>
<p>Here, we do the same thing: iterating until we find the correct place, but also keeping track of the parent node. Then, we insert the node as either the left or the right child of the parent, depending on its value.</p>
<h4 id="heading-time-and-space-complexity-8">Time and space complexity</h4>
<p>The time complexity is again \(O(h)\) (<em>or if the tree is unbalanced,</em> \(O(n)\)) for the same reason as in the recursive solution. But the space complexity is constant – \(O(1)\) – as we only use pointers.</p>
<h4 id="heading-deleting-from-a-binary-search-tree">Deleting from a binary search tree</h4>
<p>The challenging thing when deleting a node from a BST is keeping the BST as a BST. All smaller values should still go to the root node's left subtree, and all those that are larger should go to the root node's right subtree.</p>
<p>Let's take a look at how we might do it in JavaScript:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deleteNode</span>(<span class="hljs-params">root: TreeNode | null, key: number</span>) </span>{
  <span class="hljs-keyword">if</span> (root === <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">return</span> root;
  }

  <span class="hljs-keyword">if</span> (key &lt; root.val) {
    root.left = deleteNode(root.left, key);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (key &gt; root.val) {
    root.right = deleteNode(root.right, key);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Node-to-be-deleted has no children</span>
    <span class="hljs-keyword">if</span> (root.left === <span class="hljs-literal">null</span> &amp;&amp; root.right === <span class="hljs-literal">null</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    } 

    <span class="hljs-comment">// If either the left or the right child exists,</span>
    <span class="hljs-comment">// return the one that exists as the new child </span>
    <span class="hljs-comment">// of the parent node (of the node-to-be-deleted)</span>
    <span class="hljs-keyword">if</span> (root.left === <span class="hljs-literal">null</span> || root.right === <span class="hljs-literal">null</span>) {
      <span class="hljs-keyword">return</span> root.left ? root.left : root.right;
    }

    <span class="hljs-comment">// If both children exist, traverse the left subtree, get its maximum value...</span>
    <span class="hljs-keyword">let</span> currentNode = root.left;

    <span class="hljs-keyword">while</span> (currentNode.right !== <span class="hljs-literal">null</span>) {
      currentNode = currentNode.right;
    }

    <span class="hljs-comment">// ...replace it with the node-to-be-deleted</span>
    root.val = currentNode.val;
    <span class="hljs-comment">// ...then apply the recursion to the left subtree to get rid of the duplicate value</span>
    root.left = deleteNode(root.left, root.val);
  }

  <span class="hljs-keyword">return</span> root;
}
</code></pre>
<p>We traverse the tree until we find the node to be deleted. Once we find it, there are several things to do.</p>
<p>In the case where it doesn't have any child nodes, we can return <code>null</code> and be done with it.</p>
<p>If it has one child node, we can return the one that exists using the ternary operation (<code>return root.left ? root.left : root.right</code>).</p>
<p><strong>Note:</strong> <em>In this case, we're essentially making the root of the subtree the child of the parent node.</em></p>
<p>For example, in the image, if the node-to-be-deleted is 10 (it has only right child node with the value 14), we make 14 the right child of 8. It doesn't break our BST, because those that are larger than 8 continue to be in the right subtree of 8:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747918477656/7a66ad05-3c93-4482-987e-399f65e9dc57.png" alt="A binary tree that has the value 8 as its root node. It has a left child with the value 3 and a right child with the value 10. The left child has a left child node that has the value 1 and a right child node that has the value 6, which has a left child node with the value 4 and a right child node with the value 7. The right child of the root node that has the value 10 has a right child node with value 14, which has a left child node that has the value 13." class="image--center mx-auto" width="876" height="830" loading="lazy"></p>
<p>Otherwise, if both the left and right children of the node-to-be-deleted exist, we need to do something different.</p>
<p>In this case, we'll replace the node-to-be-deleted with the largest value in the left subtree.</p>
<p>But, after replacing, we'll have two nodes of the same value in both places, so we need to apply <code>deleteNode</code> itself to the subtree that we've taken our replacement node from.</p>
<p>This is all done to keep the BST as BST. It might be a bit difficult to wrap your head around at first, but <a target="_blank" href="https://www.youtube.com/watch?v=LFzAoJJt92M">NeetCode has a detailed explanation of this problem</a>.</p>
<p><strong>Note</strong> that we can also use the smallest value in the right subtree as well. In that case, our code would look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> currentNode = root.right;

<span class="hljs-keyword">while</span> (currentNode.left !== <span class="hljs-literal">null</span>) {
  currentNode = currentNode.left;
}

root.val = currentNode.val;
root.right = deleteNode(root.right, root.val);
</code></pre>
<h4 id="heading-time-and-space-complexity-9">Time and space complexity</h4>
<p>Similar to inserting into a BST, both the time and space complexity of deleting from a BST will be \(O(h)\) where \(h\) is the height of the tree.</p>
<h4 id="heading-traversals">Traversals</h4>
<p>We'll take a brief look at two of the most famous ways to traverse a tree where the order in which we visit the nodes matters: depth-first search and breadth-first search.</p>
<h5 id="heading-1-depth-first-search-dfs">1. Depth-First Search (DFS)</h5>
<p>In a depth-first search, we traverse through a branch until we get to a leaf node. Then, we backtrack and do the same thing with another branch.</p>
<p>There are three common ways to do a depth-first search:</p>
<ul>
<li><p>preorder traversal</p>
</li>
<li><p>inorder traversal</p>
</li>
<li><p>postorder traversal</p>
</li>
</ul>
<h6 id="heading-preorder-traversal">Preorder traversal:</h6>
<p>It goes like this: We first visit the node, then go on to its left subtree, then the right subtree.</p>
<p><strong>node ➞ left subtree ➞ right subtree</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747918872908/dedfc341-d32e-4558-abd6-00ff8d67b46f.gif" alt="Animated visualization of the same binary search tree displaying preorder traversal, with highlighted nodes in this order: 8, 3, 1, 6, 4, 7, 10, 14, 13." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>We can do a preorder walk recursively:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">preorderWalk</span>(<span class="hljs-params">node</span>) </span>{
  <span class="hljs-keyword">if</span> (node === <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-built_in">console</span>.log(node.val);
  preorderWalk(node.left);
  preorderWalk(node.right);
}
</code></pre>
<h6 id="heading-inorder-traversal">Inorder traversal:</h6>
<p>It goes like this: we first visit the left subtree, then the node, then the right subtree.</p>
<p><strong>left subtree ➞ node ➞ right subtree</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747918945108/a1ff3284-8aa0-4815-928c-99cdbd8ac956.gif" alt="Animated visualization of the same binary search tree displaying inorder traversal, with highlighted nodes in this order: 1, 3, 4, 6, 7, 8, 10, 13, 14." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p><strong>Note:</strong> The inorder traversal gives us the sorted values.</p>
<p>We can do an inorder walk recursively as well:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inorderWalk</span>(<span class="hljs-params">node</span>) </span>{
  <span class="hljs-keyword">if</span> (node === <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">return</span>;
  }

  inorderWalk(node.left);
  <span class="hljs-built_in">console</span>.log(node.val);
  inorderWalk(node.right);
}
</code></pre>
<h6 id="heading-postorder-traversal">Postorder traversal:</h6>
<p>It goes like this: we first visit the left subtree, then the right subtree, and finally the node.</p>
<p><strong>left subtree ➞ right subtree ➞ node</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747919023258/e3c7f4a3-3ec8-47d3-aa49-e8f4b0f8d30a.gif" alt="Animated visualization of the same binary search tree displaying postorder traversal, with highlighted nodes in this order: 1, 4, 7, 6, 3, 13, 14, 10, 8." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>We can do a postorder walk recursively:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postorderWalk</span>(<span class="hljs-params">node</span>) </span>{
  <span class="hljs-keyword">if</span> (node === <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">return</span>;
  }

  postorderWalk(node.left);
  postorderWalk(node.right);
  <span class="hljs-built_in">console</span>.log(node.val);
}
</code></pre>
<h5 id="heading-2-breadth-first-search-bfs">2. Breadth-First Search (BFS)</h5>
<p>In breadth-first search, we visit the nodes level by level, that is, visiting every child of a node first before moving on.</p>
<p>A queue is used when implementing a BFS. Since we don't have edges connecting all the children on one level together, it makes sense to keep them in a queue and visit each one when their time comes. When a node is added to the queue and has not been visited yet, it's called a <strong>discovered node</strong>.</p>
<p>A simple BFS operation looks like this (which is repeated until the queue is empty):</p>
<ul>
<li><p>visit node</p>
</li>
<li><p>enqueue left child</p>
</li>
<li><p>enqueue right child</p>
</li>
</ul>
<p>Note that the breadth-first search is also known as <em>level-order traversal</em>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747919081789/17beb70f-7302-4f32-9a9a-72d69e190ac9.gif" alt="Animated visualization of the same binary search tree displaying level-order traversal, with highlighted nodes in this order: 8, 3, 10, 1, 6, 14, 4, 7, 13." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>A simple example of a level-order traversal in JavaScript might look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">levelOrderWalk</span>(<span class="hljs-params">root</span>) </span>{
  <span class="hljs-keyword">if</span> (root === <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-keyword">let</span> queue = [];
  queue.push(root);

  <span class="hljs-keyword">while</span> (queue.length &gt; <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">let</span> currentNode = queue[<span class="hljs-number">0</span>];

    <span class="hljs-built_in">console</span>.log(currentNode.val);

    <span class="hljs-keyword">if</span> (currentNode.left !== <span class="hljs-literal">null</span>) {
      queue.push(currentNode.left);
    }

    <span class="hljs-keyword">if</span> (currentNode.right !== <span class="hljs-literal">null</span>) {
      queue.push(currentNode.right);
    }

    <span class="hljs-comment">// Remove the current node</span>
    queue.shift();
  }
}
</code></pre>
<p>This example is based on Vaidehi Joshi's <a target="_blank" href="https://gist.github.com/vaidehijoshi/27f9fa6b6b68f70360019805b5ca3692#file-level_order_search-js">GitHub Gist</a>.</p>
<h2 id="heading-chapter-eight-heap-priority-queue">Chapter Eight: Heap / Priority Queue</h2>
<p>It’s now time to take a look at a data structure called a <em>heap</em>, which is a great way to implement an <a target="_blank" href="https://en.wikipedia.org/wiki/Abstract_data_type">abstract data type</a> called a <strong>priority queue</strong>. They're so interrelated that priority queues are sometimes referred to as heaps – because heaps are a very efficient way to create a priority queue.</p>
<h3 id="heading-heap-properties">Heap properties</h3>
<p>The kind of heap we're interested in is also called a <strong>binary heap</strong> because it's just a binary tree that has specific properties.</p>
<p>One of them is that it must be a <strong>complete binary tree</strong>, meaning that all the levels must be filled, and <em>all nodes in the last level should be as far left as possible</em>.</p>
<p>For example, when it comes to shape, this is a complete binary tree:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747919274226/cdc2f987-3327-4220-8584-ad3999ea7f39.gif" alt="Animated visualization of a tree with root node having two children, both of its left and right children having two children their own. The left child of the left child has only a left child on its own." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>But heaps must also be either a <strong>max heap</strong> or a <strong>min heap</strong> – all the parent nodes must be either greater than or equal to the values of their children (if it's a max heap) or less than or equal to the values of their children (if it's a min heap).</p>
<p>A max heap might look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747919301787/6a3b30cd-a686-4112-80a9-0249c7597751.gif" alt="Animated visualization of a max heap, having 42 at the top, 19 at its left, 36 at its right. 19 has 17 at its left, 3 at its right. 36 has 25 at its left, 1 at its right. 17 has 2 at its left." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p><strong>Note:</strong> A left child doesn't have to be less than the right child at all, as in a binary search tree. Also, we can always have duplicate values in a heap.</p>
<p>A min heap, on the other hand, has the values of parent nodes less than those of their children:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747919335031/b5b82848-ac1f-405d-9124-725a9c26af55.gif" alt="Animated visualization of a min heap, having 1 at the top, 2 at its left, 3 at its right. 2 has 17 at its left, 19 at its right. 3 has 36 at its left, 7 at its right. 17 has 42 at its left." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p><strong>Note:</strong> When we have a max heap, the root node will have the maximum value. And, if we have a min heap instead, the root node will have the minimum value.</p>
<h3 id="heading-heaps-with-arrays">Heaps with arrays</h3>
<p>We can create a heap using an array. Since the root node is the most interesting element with either a maximum or minimum value, it'll be the first element in our array, residing at the 0th index.</p>
<p>What's nice about using an array is that, given a parent node's index \(i\), its left child will be at the index \(2i + 1\), and its right child will be at the index \(2i + 2\).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747919415922/b98a55a9-f9cc-4e11-8d53-f5a8a411d861.gif" alt="Animated visualization of the max heap above implemented as an array. A left child's index corresponds to 2i+1, a right child's index corresponds to 2i+2." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>Given that, any child node's parent will be at the index \(\lfloor{\frac{(n - 1)}{2}}\rfloor\).</p>
<p><strong>Note:</strong> \(\lfloor\) and \(\rfloor\)indicate the <a target="_blank" href="https://en.wikipedia.org/wiki/Floor_and_ceiling_functions">floor function</a>.</p>
<p>One question we might ask at this moment is that why should we use an array at all?</p>
<p>The answer lies in the word <strong>queue</strong> of a <strong>priority queue</strong>. Since a queue is mainly concerned with the first element (following the <a target="_blank" href="https://en.wikipedia.org/wiki/FIFO_\(computing_and_electronics\)">FIFO principle</a>), an array can be an ideal choice. In a priority queue, each element has a priority, and the value with the highest priority is dequeued first.</p>
<h3 id="heading-insertingremoving-elements">Inserting/removing elements</h3>
<p>Let's take a look at how we can add an element to a heap.</p>
<p>We know that we have to add the new element to the bottom leftmost place, but once we do that, it might violate the max heap or the min heap property. Then, how can we avoid violating the <strong>heap-order property</strong>?</p>
<p>We'll <strong>heapify</strong>, of course!</p>
<p>Let's say that we want to add a node with the value <code>20</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747919523941/eb0959dd-fcf8-4fb7-9ebf-938fde24ba10.gif" alt="Animated visualization of the max heap above. A new item 20 is first inserted at the leftest possible place. Then it swaps places with 17 and then 19, coming to the left of 42." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>So, heapify is the swapping of nodes until we know that the heap-order property is maintained.</p>
<p>A similar thing happens when we need to remove an element. But since we're mainly concerned with the maximum or the minimum element, we just need to remove the root node. So, how are we going to do that?</p>
<p>We start off by swapping the last element (the bottom leftmost one) with the root. Now we can easily remove the "root," which resides as a leaf node. But we still need to maintain the heap-order property, so we need to <strong>heapify</strong> again.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747919564248/0e7b9a38-6b23-4448-a804-cf095d16f30e.gif" alt="Animated visualization of the max heap above. 2 swapping places with 42, 42 being disappeared. 2 later swaps places with 36 and 25, now 36 coming to the top." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<h3 id="heading-heapsort">Heapsort</h3>
<p>Even better thing is that if we have a heap, and continually heapify it, we can sort an array.</p>
<p>Let's build a max heap first:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">buildMaxHeap</span>(<span class="hljs-params">arr: <span class="hljs-built_in">number</span>[]</span>) </span>{
  <span class="hljs-comment">/*
  Index of the last internal node 
  (i.e., the parent of the last leaf node, 
   or, the last non-leaf node).
  The last leaf node will reside at index arr.length - 1,
  so, we're getting its parent using the formula mentioned above.
  */</span>
  <span class="hljs-keyword">let</span> i = <span class="hljs-built_in">Math</span>.floor((arr.length - <span class="hljs-number">1</span>) / <span class="hljs-number">2</span>);

  <span class="hljs-keyword">while</span> (i &gt;= <span class="hljs-number">0</span>) {
    heapify(arr, i, arr.length);
    i--;
  }

  <span class="hljs-keyword">return</span> arr;
}
</code></pre>
<p>Then, the <code>heapify</code> function:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">heapify</span>(<span class="hljs-params">arr: <span class="hljs-built_in">number</span>[], i: <span class="hljs-built_in">number</span>, maxLength: <span class="hljs-built_in">number</span></span>) </span>{
  <span class="hljs-keyword">while</span> (i &lt; maxLength) {
    <span class="hljs-keyword">let</span> index = i;
    <span class="hljs-keyword">let</span> leftChildIdx = <span class="hljs-number">2</span> * i + <span class="hljs-number">1</span>;
    <span class="hljs-keyword">let</span> rightChildIdx = leftChildIdx + <span class="hljs-number">1</span>;

    <span class="hljs-keyword">if</span> (leftChildIdx &lt; maxLength &amp;&amp; arr[leftChildIdx] &gt; arr[index]) {
      index = leftChildIdx;
    }

    <span class="hljs-keyword">if</span> (rightChildIdx &lt; maxLength &amp;&amp; arr[rightChildIdx] &gt; arr[index]) {
      index = rightChildIdx;
    }

    <span class="hljs-keyword">if</span> (index === i) { <span class="hljs-keyword">return</span>; }

    <span class="hljs-comment">// Swap</span>
    [arr[i], arr[index]] = [arr[index], arr[i]];

    i = index;
  }
}
</code></pre>
<p>With a given index <code>i</code>, we get its left and right children indices, and if the indices are within bounds, we check if they are out of order. In that case, we make the <code>index</code> the index of the child, and swap the two nodes. Then, we continue with that new index, assigning it to <code>i</code>.</p>
<p>Now, <code>heapify</code> is nice and all, but how can we actually use it for sorting?</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">heapSort</span>(<span class="hljs-params">arr: <span class="hljs-built_in">number</span>[]</span>) </span>{
  buildMaxHeap(arr);

  <span class="hljs-keyword">let</span> lastElementIdx = arr.length - <span class="hljs-number">1</span>;

  <span class="hljs-keyword">while</span> (lastElementIdx &gt; <span class="hljs-number">0</span>) {
    [arr[<span class="hljs-number">0</span>], arr[lastElementIdx]] = [arr[lastElementIdx], arr[<span class="hljs-number">0</span>]];

    heapify(arr, <span class="hljs-number">0</span>, lastElementIdx);
    lastElementIdx--;
  }

  <span class="hljs-keyword">return</span> arr;
}
</code></pre>
<p><strong>Note</strong> that our max heap <code>[42, 19, 36, 17, 3, 25, 1, 2]</code> won't change when used in the <code>buildMaxHeap</code> function, as it's already a max heap! But if it were to have <code>17</code> as the right child of <code>42</code>, then <code>17</code> would have <code>25</code> as a child, which breaks the heap-order property. So, using <code>buildMaxHeap</code> with this broken version will correctly swap the <code>17</code> and <code>25</code>, making it a max heap:</p>
<pre><code class="lang-typescript">buildMaxHeap([<span class="hljs-number">42</span>, <span class="hljs-number">36</span>, <span class="hljs-number">17</span>, <span class="hljs-number">19</span>, <span class="hljs-number">3</span>, <span class="hljs-number">25</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>]);

<span class="hljs-comment">// -&gt; [42, 36, 25, 19, 3, 17, 1, 2]</span>
</code></pre>
<p>In <code>heapSort</code>, with our newly built max heap, we'll start with swapping the first and last nodes. Then, we'll keep heapifying until we get all the elements in their place. If we use it with our very own max heap, we can see that it returns the sorted array:</p>
<pre><code class="lang-typescript">heapSort([<span class="hljs-number">42</span>, <span class="hljs-number">19</span>, <span class="hljs-number">36</span>, <span class="hljs-number">17</span>, <span class="hljs-number">3</span>, <span class="hljs-number">25</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>]);
<span class="hljs-comment">// -&gt; [1, 2, 3, 17, 19, 25, 36, 42]</span>
</code></pre>
<p>The examples are adapted from <a target="_blank" href="https://medium.com/basecs/heapify-all-the-things-with-heap-sort-55ee1c93af82">Vaidehi Joshi's article</a>.</p>
<h4 id="heading-time-and-space-complexity-10">Time and space complexity</h4>
<p>Heap sort, as a nice sorting algorithm it is, runs in \(O(n \ log \ n)\) time.</p>
<p>In this example, building the max heap starts from the last non-leaf node and goes up to the root node, each time calling <code>heapify</code>. The <code>heapify</code> function has a time complexity of \(O(log \ n)\) as we're working with a binary tree, and in the worst case, we get to do it for all the levels. Since we do it \(n / 2\) times, overall, <code>buildMaxHeap</code> has \(O(n \ log \ n)\) time complexity.</p>
<p>We're swapping the first and last elements, and heapifying as we go through each element, so this is also overall an \(O(n \ log \ n)\) operation — which makes the time complexity of <code>heapSort</code> \(O(n \ log \ n)\)<em>.</em></p>
<p><strong>Note:</strong> Building the max heap <a target="_blank" href="https://stackoverflow.com/questions/9755721/how-can-building-a-heap-be-on-time-complexity">can be improved to have</a> \(O(n)\) <a target="_blank" href="https://stackoverflow.com/questions/9755721/how-can-building-a-heap-be-on-time-complexity">runtime</a>.</p>
<p>Since there is no use of auxiliary space, the space complexity is constant, \(O(1)\).</p>
<h2 id="heading-chapter-nine-backtracking">Chapter Nine: Backtracking</h2>
<p>Let's start with admitting this one fact: backtracking is hard. Or rather, <em>understanding it the first time</em> is hard. Or, it's one of those concepts that you think you grasped it, only to realize later that you actually didn't.</p>
<p>We'll focus on one problem of finding the subsets of an array, but before that, let's imagine that we're walking along a path.</p>
<p>Then, we reach a fork. We pick one of the paths, and walk.</p>
<p>Then, we reach another fork in the path. We pick one of the paths again, and go on walking, then we reach a dead end. So, we <em>backtrack</em> to the last point we had a fork, then go through the other path that we didn't choose the first time.</p>
<p>Then we reach another dead end. So, we <em>backtrack</em> once more and realize that there are no other paths we can go from there. So we <em>backtrack</em> again, and explore the other path we didn't choose the first time we came to this point.</p>
<p>We reach yet another dead end, so we <em>backtrack</em>. We see that there are no more paths to explore, so we <em>backtrack</em> once more.</p>
<p>Now, we're at our starting point. There are no more paths left to explore, so we can stop walking.</p>
<p>It was a nice but tiring walk, and it went like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747920157477/3151155c-d40c-408d-bff6-5d326ad0a0f3.gif" alt="Animated visualization of the concept of backtracking." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>Now, let's take a look at a LeetCode problem.</p>
<h3 id="heading-subsets">Subsets</h3>
<p>The description for <a target="_blank" href="https://leetcode.com/problems/subsets">Subsets</a> says:</p>
<blockquote>
<p>Given an integer array <code>nums</code> of <strong>unique</strong> elements, return <em>all possible subsets (the power set)</em>.</p>
<p>The solution set <strong>must not</strong> contain duplicate subsets. Return the solution in <strong>any order</strong>.</p>
</blockquote>
<p>For example:</p>
<pre><code class="lang-plaintext">Input: nums = [1, 2, 3]
Output: [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
</code></pre>
<p>Or:</p>
<pre><code class="lang-plaintext">Input: nums = [0]
Output: [[], [0]]
</code></pre>
<p>Before diving into the solution code, let's take a look at how backtracking will work in this case. Let's call the <code>nums</code> array <code>items</code> instead:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747920218696/a85bf516-9bc1-4231-ab39-4a31ce1a8e6d.gif" alt="Animated visualization of backtracking for an array [1, 2, 3], exploring each possible choice." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>For each item in <code>items</code>, we have initially two choices: to include the item, or not to include it.</p>
<p>For each level \(n\) in this <em>decision tree</em>, we have the option to include the next item in <code>items</code>. We have \(2^n\) possible subsets in total.</p>
<p>Let's simplify the example a bit, and say that <code>items</code> is now <code>['a', 'b']</code> (<strong>We'll ignore the problem specifics for now</strong>).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747920275978/ab435765-7b05-4939-bd72-55dcfae7a6d4.gif" alt="Animated visualization of backtracking for an array ['a', 'b'], exploring each possible choice." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>In this case, we can use backtracking like this:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">subsets</span>(<span class="hljs-params">items: <span class="hljs-built_in">string</span>[]</span>) </span>{
  <span class="hljs-keyword">let</span> result: <span class="hljs-built_in">string</span>[][] = [];
  <span class="hljs-keyword">let</span> currentSubset: <span class="hljs-built_in">string</span>[] = [];

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">backtrack</span>(<span class="hljs-params">idx: <span class="hljs-built_in">number</span></span>) </span>{
    <span class="hljs-keyword">if</span> (idx &gt;= items.length) {
      result.push([...currentSubset]);
      <span class="hljs-keyword">return</span>;
    }

    currentSubset.push(items[idx]);
    backtrack(idx + <span class="hljs-number">1</span>);

    currentSubset.pop();
    backtrack(idx + <span class="hljs-number">1</span>);
  }

  backtrack(<span class="hljs-number">0</span>);

  <span class="hljs-keyword">return</span> result;
}

<span class="hljs-built_in">console</span>.log(subsets([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>]));
<span class="hljs-comment">// -&gt; [['a', 'b'], ['a'], ['b'], []]</span>
</code></pre>
<p>Well, it looks simple at first glance, but what's going on?</p>
<p>One thing to notice is that we <code>pop</code> from the <code>currentSubset</code>, then call <code>backtrack</code>. In our example of walking, that's the part we go back to our previous point, and continue our walk.</p>
<p>In the first animation, we indicated a dead end with a cross mark, and in this case, a dead end is the <strong>base case</strong> we reach.</p>
<p>It might still be tough to understand, so let's add some helpful <code>console.log</code>s, and see the output:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">subsets</span>(<span class="hljs-params">items: <span class="hljs-built_in">string</span>[]</span>) </span>{
  <span class="hljs-keyword">let</span> result: <span class="hljs-built_in">string</span>[][] = [];
  <span class="hljs-keyword">let</span> currentSubset: <span class="hljs-built_in">string</span>[] = [];

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">backtrack</span>(<span class="hljs-params">idx: <span class="hljs-built_in">number</span></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`======= this is backtrack(<span class="hljs-subst">${<span class="hljs-built_in">arguments</span>[<span class="hljs-number">0</span>]}</span>) =======`</span>)
    <span class="hljs-keyword">if</span> (idx &gt;= items.length) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`idx is <span class="hljs-subst">${idx}</span>, currentSubset is [<span class="hljs-subst">${currentSubset}</span>], adding it to result...`</span>);
      result.push([...currentSubset]);
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`backtrack(<span class="hljs-subst">${<span class="hljs-built_in">arguments</span>[<span class="hljs-number">0</span>]}</span>) is returning...\n`</span>)
      <span class="hljs-keyword">return</span>;
    }

    currentSubset.push(items[idx]);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`added <span class="hljs-subst">${items[idx]}</span> to currentSubset, inside backtrack(<span class="hljs-subst">${<span class="hljs-built_in">arguments</span>[<span class="hljs-number">0</span>]}</span>)`</span>);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`calling backtrack(<span class="hljs-subst">${idx + <span class="hljs-number">1</span>}</span>)...`</span>)
    backtrack(idx + <span class="hljs-number">1</span>);

    <span class="hljs-keyword">let</span> item = currentSubset.pop();
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`popped <span class="hljs-subst">${item}</span> from currentSubset, inside backtrack(<span class="hljs-subst">${<span class="hljs-built_in">arguments</span>[<span class="hljs-number">0</span>]}</span>)`</span>);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`calling backtrack(<span class="hljs-subst">${idx + <span class="hljs-number">1</span>}</span>)...`</span>)
    backtrack(idx + <span class="hljs-number">1</span>);

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`******* done with backtrack(<span class="hljs-subst">${<span class="hljs-built_in">arguments</span>[<span class="hljs-number">0</span>]}</span>) *******\n`</span>);
  }

  backtrack(<span class="hljs-number">0</span>);

  <span class="hljs-keyword">return</span> result;
}

<span class="hljs-built_in">console</span>.log(subsets([<span class="hljs-string">'a'</span>, <span class="hljs-string">'b'</span>]));
</code></pre>
<p>The output looks like this:</p>
<pre><code class="lang-plaintext">======= this is backtrack(0) =======
added a to currentSubset, inside backtrack(0)
calling backtrack(1)...
======= this is backtrack(1) =======
added b to currentSubset, inside backtrack(1)
calling backtrack(2)...
======= this is backtrack(2) =======
idx is 2, currentSubset is [a,b], adding it to result...
backtrack(2) is returning...

popped b from currentSubset, inside backtrack(1)
calling backtrack(2)...
======= this is backtrack(2) =======
idx is 2, currentSubset is [a], adding it to result...
backtrack(2) is returning...

******* done with backtrack(1) *******

popped a from currentSubset, inside backtrack(0)
calling backtrack(1)...
======= this is backtrack(1) =======
added b to currentSubset, inside backtrack(1)
calling backtrack(2)...
======= this is backtrack(2) =======
idx is 2, currentSubset is [b], adding it to result...
backtrack(2) is returning...

popped b from currentSubset, inside backtrack(1)
calling backtrack(2)...
======= this is backtrack(2) =======
idx is 2, currentSubset is [], adding it to result...
backtrack(2) is returning...

******* done with backtrack(1) *******

******* done with backtrack(0) *******

[ [ 'a', 'b' ], [ 'a' ], [ 'b' ], [] ]
</code></pre>
<p>If you noticed, <em>Add</em> <code>'a'</code>? and <em>Go ahead?</em> arrows on the first level are calls to <code>backtrack(0)</code>.</p>
<p><em>Add</em> <code>'b'</code>? and <em>Go ahead?</em> arrows on the second level are calls to <code>backtrack(1)</code>.</p>
<p><code>backtrack(2)</code> calls are when we reach the "dead ends". In those cases, we add <code>currentSubset</code> to the <code>result</code>. We always reach the base case in a <code>backtrack(2)</code> call because it's only when the <code>idx</code> equals <code>items.length</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747920409397/0c18c7a6-1776-415b-8918-a6cafe6ba70c.gif" alt="Animated visualization of backtrack function for the array ['a', 'b']." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p><strong>Note:</strong> We modified the function in the above examples to work with strings, but in the actual solution we'll only deal with numbers, so in TypeScript, <code>result</code> and <code>currentSubset</code> will look like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> result: <span class="hljs-built_in">number</span>[][] = [];
<span class="hljs-keyword">let</span> currentSubset: <span class="hljs-built_in">number</span>[] = [];
</code></pre>
<p>Also, the function parameter and return types are different:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">subsets</span>(<span class="hljs-params">nums: <span class="hljs-built_in">number</span>[]</span>): <span class="hljs-title">number</span>[][] </span>{ ... }
</code></pre>
<p>Otherwise, everything stays the same.</p>
<h4 id="heading-time-and-space-complexity-11">Time and space complexity</h4>
<p>A subset is, in the worst case, length \(n\) which is the length of our input. We'll have \(2^n\) subsets and since we also use a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax">spread operator</a> in our example to copy <code>currentSubset</code>, the time complexity will be \(O(n \cdot 2^n)\). The space complexity is – <em>I think</em> – \(O(n \cdot 2^n)\) as well because of the recursive call stack (which is of depth <code>n</code>), and the space needed for <code>result</code> (which is in the worst case \(2^n\)).</p>
<h2 id="heading-chapter-ten-tries">Chapter Ten: Tries</h2>
<p>The trie data structure <a target="_blank" href="https://en.wikipedia.org/wiki/Trie#History,_etymology,_and_pronunciation">gets its name from the word <em>re<strong><strong>trie</strong></strong>val</em></a> – and it's usually pronounced as "try," so that we don't get confused with another familiar and friendly data structure, "tree."</p>
<p>But a trie is still a tree (or tree-like) data structure whose nodes usually store individual letters. So, by traversing the nodes in a trie, we can retrieve strings.</p>
<p>Tries are useful for applications such as autocompletion and spellchecking – and the larger our trie is, the less work we have to do for inserting a new value.</p>
<p><strong>Note:</strong> Using arrays is not very memory-efficient, but for now, we'll stick to the array implementation.</p>
<p>First, let's see what a trie looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747920685051/e152eedd-75c6-478b-8291-5510b8f1421c.gif" alt="Animated visualization of a trie having the values &quot;sea&quot; and &quot;see&quot;" class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>In this trie, we can retrieve the strings "sea" and "see" – but not "sew", for example.</p>
<p>There is a lot going on, but we can try to understand it piece by piece.</p>
<p>Let's look at a trie node.</p>
<p>We'll create a <code>TrieNode</code> class that has <code>children</code>, which is an array of length 26 (so that each index corresponds to a letter in the English alphabet), and a flag variable <code>isEndOfWord</code> to indicate whether that node represents the last character of a word:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> TrieNode {
  children: (TrieNode | <span class="hljs-literal">null</span>)[];
  isEndOfWord: <span class="hljs-built_in">boolean</span>;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
    <span class="hljs-built_in">this</span>.children = <span class="hljs-built_in">Array</span>.from({ length: <span class="hljs-number">26</span> }, <span class="hljs-function">() =&gt;</span> <span class="hljs-literal">null</span>);
    <span class="hljs-built_in">this</span>.isEndOfWord = <span class="hljs-literal">false</span>;
  }
}
</code></pre>
<p>We're initializing <code>children</code> with <code>null</code> values. As we add a character to our trie, the index that corresponds to that character will be filled.</p>
<p><strong>Note:</strong> We're not storing the actual character itself in this implementation – it's implicit in the usage of indices.</p>
<p>In a trie, we start with an empty root node.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> Trie {
  root: TrieNode;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
    <span class="hljs-built_in">this</span>.root = <span class="hljs-keyword">new</span> TrieNode();
  }
  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>To insert a word, we're going to loop through each character, and initialize a new <code>TrieNode</code> to the corresponding index.</p>
<pre><code class="lang-typescript">insert(word: <span class="hljs-built_in">string</span>) {
  <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.root;
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> char <span class="hljs-keyword">of</span> word) {
    <span class="hljs-keyword">let</span> idx = char.charCodeAt(<span class="hljs-number">0</span>) - <span class="hljs-string">'a'</span>.charCodeAt(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">if</span> (currentNode.children[idx] === <span class="hljs-literal">null</span>) {
      currentNode.children[idx] = <span class="hljs-keyword">new</span> TrieNode();
    }
    currentNode = currentNode.children[idx];
  }

  currentNode.isEndOfWord = <span class="hljs-literal">true</span>;
}
</code></pre>
<p>Once we reach the node that indicates the last character of the word we inserted, we also mark the <code>isEndOfWord</code> variable as <code>true</code>.</p>
<p><strong>Note:</strong> <code>word</code> is going to be lowercase in these examples – otherwise, we have to convert it, such as:</p>
<pre><code class="lang-typescript">word = word.toLowerCase();
</code></pre>
<p>For searching a word's existence in the trie, we'll do a similar thing. We'll look at the nodes for each character, and if we reach the last one that has <code>isEndOfWord</code> marked as <code>true</code>. That means we've found the word:</p>
<pre><code class="lang-typescript">search(word: <span class="hljs-built_in">string</span>) {
  <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.root;
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> char <span class="hljs-keyword">of</span> word) {
    <span class="hljs-keyword">let</span> idx = char.charCodeAt(<span class="hljs-number">0</span>) - <span class="hljs-string">'a'</span>.charCodeAt(<span class="hljs-number">0</span>);
    <span class="hljs-keyword">if</span> (currentNode.children[idx] === <span class="hljs-literal">null</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }      
    currentNode = currentNode.children[idx];
  }

  <span class="hljs-keyword">return</span> currentNode.isEndOfWord;
}
</code></pre>
<p><strong>Note:</strong> If we find the word we're looking for, then it's called a <strong>search hit</strong>. Otherwise, we have a <strong>search miss</strong> and the word doesn't exist in our trie.</p>
<p>Removing a word is a bit more challenging. Let's say that we want to remove the word "see." But, there is also another word "sea," with the same prefix ('s' and 'e'). So, we should remove only the nodes that we're allowed to.</p>
<p>For this reason, we'll define a recursive function. Once we reach the last character of the word we want to remove, we'll back up and remove the characters we can remove:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> removeRecursively = <span class="hljs-function">(<span class="hljs-params">node: TrieNode | <span class="hljs-literal">null</span>, word: <span class="hljs-built_in">string</span>, depth: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (node === <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }

  <span class="hljs-keyword">if</span> (depth === word.length) {
    <span class="hljs-keyword">if</span> (node.isEndOfWord) {
      node.isEndOfWord = <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">if</span> (node.children.every(<span class="hljs-function"><span class="hljs-params">child</span> =&gt;</span> child === <span class="hljs-literal">null</span>)) {
      node = <span class="hljs-literal">null</span>;
    }

    <span class="hljs-keyword">return</span> node;
  }

  <span class="hljs-keyword">let</span> idx = word[depth].charCodeAt(<span class="hljs-number">0</span>) - <span class="hljs-string">'a'</span>.charCodeAt(<span class="hljs-number">0</span>);
  node.children[idx] = removeRecursively(node.children[idx], word, depth + <span class="hljs-number">1</span>);

  <span class="hljs-keyword">if</span> (node.children.every(<span class="hljs-function"><span class="hljs-params">child</span> =&gt;</span> child === <span class="hljs-literal">null</span>) &amp;&amp; !node.isEndOfWord) {
    node = <span class="hljs-literal">null</span>;
  }

  <span class="hljs-keyword">return</span> node;
}
</code></pre>
<p><code>depth</code> indicates the index of the word, or <em>the depth of the trie we reach</em>.</p>
<p>Once <code>depth</code> is equal to the word's length (one past the last character), we check if it's the end of the word. If that's the case, we'll mark it as <code>false</code> now, because that word won't exist from here on. Then, we can only mark the node as <code>null</code> if it doesn't have any children (in other words, if all of them are <code>null</code>). We'll apply this logic to each child node recursively until the word is removed as far as it can be removed.</p>
<p>Here is the final example implementation of a trie:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> TrieNode {
  children: (TrieNode | <span class="hljs-literal">null</span>)[];
  isEndOfWord: <span class="hljs-built_in">boolean</span>;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
    <span class="hljs-built_in">this</span>.children = <span class="hljs-built_in">Array</span>.from({ length: <span class="hljs-number">26</span> }, <span class="hljs-function">() =&gt;</span> <span class="hljs-literal">null</span>);
    <span class="hljs-built_in">this</span>.isEndOfWord = <span class="hljs-literal">false</span>;
  }
}

<span class="hljs-keyword">class</span> Trie {
  root: TrieNode;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
    <span class="hljs-built_in">this</span>.root = <span class="hljs-keyword">new</span> TrieNode();
  }

  insert(word: <span class="hljs-built_in">string</span>) {
    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.root;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> char <span class="hljs-keyword">of</span> word) {
      <span class="hljs-keyword">let</span> idx = char.charCodeAt(<span class="hljs-number">0</span>) - <span class="hljs-string">'a'</span>.charCodeAt(<span class="hljs-number">0</span>);
      <span class="hljs-keyword">if</span> (currentNode.children[idx] === <span class="hljs-literal">null</span>) {
        currentNode.children[idx] = <span class="hljs-keyword">new</span> TrieNode();
      }
      currentNode = currentNode.children[idx];
    }

    currentNode.isEndOfWord = <span class="hljs-literal">true</span>;
  }

  search(word: <span class="hljs-built_in">string</span>) {
    <span class="hljs-keyword">let</span> currentNode = <span class="hljs-built_in">this</span>.root;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> char <span class="hljs-keyword">of</span> word) {
      <span class="hljs-keyword">let</span> idx = char.charCodeAt(<span class="hljs-number">0</span>) - <span class="hljs-string">'a'</span>.charCodeAt(<span class="hljs-number">0</span>);
      <span class="hljs-keyword">if</span> (currentNode.children[idx] === <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }      
      currentNode = currentNode.children[idx];
    }

    <span class="hljs-keyword">return</span> currentNode.isEndOfWord;
  }

  remove(word: <span class="hljs-built_in">string</span>) {
    <span class="hljs-keyword">const</span> removeRecursively = <span class="hljs-function">(<span class="hljs-params">node: TrieNode | <span class="hljs-literal">null</span>, word: <span class="hljs-built_in">string</span>, depth: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (node === <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }

      <span class="hljs-keyword">if</span> (depth === word.length) {
        <span class="hljs-keyword">if</span> (node.isEndOfWord) {
          node.isEndOfWord = <span class="hljs-literal">false</span>;
        }
        <span class="hljs-keyword">if</span> (node.children.every(<span class="hljs-function"><span class="hljs-params">child</span> =&gt;</span> child === <span class="hljs-literal">null</span>)) {
          node = <span class="hljs-literal">null</span>;
        }

        <span class="hljs-keyword">return</span> node;
      }

      <span class="hljs-keyword">let</span> idx = word[depth].charCodeAt(<span class="hljs-number">0</span>) - <span class="hljs-string">'a'</span>.charCodeAt(<span class="hljs-number">0</span>);
      node.children[idx] = removeRecursively(node.children[idx], word, depth + <span class="hljs-number">1</span>);

      <span class="hljs-keyword">if</span> (node.children.every(<span class="hljs-function"><span class="hljs-params">child</span> =&gt;</span> child === <span class="hljs-literal">null</span>) &amp;&amp; !node.isEndOfWord) {
        node = <span class="hljs-literal">null</span>;
      }

      <span class="hljs-keyword">return</span> node;
    }

    removeRecursively(<span class="hljs-built_in">this</span>.root, word, <span class="hljs-number">0</span>);
  }
}

<span class="hljs-keyword">let</span> t = <span class="hljs-keyword">new</span> Trie();

t.insert(<span class="hljs-string">'sea'</span>);
t.insert(<span class="hljs-string">'see'</span>);

<span class="hljs-built_in">console</span>.log(t.search(<span class="hljs-string">'sea'</span>)); <span class="hljs-comment">// true</span>
<span class="hljs-built_in">console</span>.log(t.search(<span class="hljs-string">'see'</span>)); <span class="hljs-comment">// true</span>

<span class="hljs-built_in">console</span>.log(t.search(<span class="hljs-string">'hey'</span>)); <span class="hljs-comment">// false</span>
<span class="hljs-built_in">console</span>.log(t.search(<span class="hljs-string">'sew'</span>)); <span class="hljs-comment">// false</span>

t.remove(<span class="hljs-string">'see'</span>);

<span class="hljs-built_in">console</span>.log(t.search(<span class="hljs-string">'see'</span>)); <span class="hljs-comment">// false </span>
<span class="hljs-built_in">console</span>.log(t.search(<span class="hljs-string">'sea'</span>)); <span class="hljs-comment">// true</span>
</code></pre>
<h4 id="heading-time-and-space-complexity-12">Time and space complexity</h4>
<p>The time complexity of creating a trie is going to be \(O(m * n)\) where \(m\) is the longest word and \(n\) is the total number of words. Inserting, searching, and deleting a word is \(O(a * n)\) where \(a\) is the length of the word and \(n\) is the total number of words.</p>
<p>When it comes to space complexity, in the worst case, each node can have children for all the characters in the alphabet we're representing. But, the size of the alphabet is constant, so the growth of storage needs will be proportionate to the number of nodes we have, which is \(O(n)\) where \(n\) is the number of nodes.</p>
<h2 id="heading-chapter-eleven-graphs">Chapter Eleven: Graphs</h2>
<p>A graph is probably <em>the</em> data structure that everyone is familiar with, regardless of their profession or interests.</p>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Graph_theory#Representation">Graph theory</a> is a very broad topic, but we'll simply look at some of the main ingredients of what makes a graph and how to represent it, as well as basic graph traversals.</p>
<p>In a graph, there are two main components: vertices (or nodes) and edges that connect those vertices.</p>
<p><strong>Note:</strong> Here, we're going to use "vertex" and "node" interchangeably. The terms "adjacent vertices" and "neighbors" are used interchangeably as well.</p>
<p>A graph can be <strong>directed</strong> or <strong>undirected</strong>. With a directed edge, we have an origin and a destination vertex. On the other hand, an undirected edge is bidirectional, origin and destination are not fixed.</p>
<p><strong>Note:</strong> There might also be <a target="_blank" href="https://en.wikipedia.org/wiki/Graph_\(discrete_mathematics\)#Mixed_graph">mixed graphs</a> that have both directed and undirected edges.</p>
<p>A graph can also be weighted or unweighted, each edge can have different weights, usually representing the cost of going from one vertex to the other.</p>
<p>We can define a graph like this:</p>
<p>$$G = (V, \ E)$$</p><p>\(V\) is a set of vertices, and \(E\) is a set of edges.</p>
<p>For example, if we have a directed graph like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747921387268/fe3d0ef9-a271-4c87-9143-84e80e41af5f.gif" alt="Animated visualization of a graph with nodes A, B, C, D. A has directed edges to B and C, C has one to B and D. D has one to C. " class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>Then, we have the vertices:</p>
<p>$$V = \{A, \ B, \ C, \ D\}$$</p><p>And, the edges are:</p>
<p>$$E = \{(A, \ B), \ (A, \ C), \ (C, \ B), \ (C, \ D)\, \ (D, \ C)\}$$</p><p>If we have an undirected graph such as this one:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747921458245/d2920859-81c0-4082-a49d-e41b08b81124.gif" alt="Animated visualization of the same graph above with undirected edges." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>We have the same vertices:</p>
<p>$$V = \{A, \ B, \ C, \ D\}$$</p><p>But our edges can look like this:</p>
<p>$$E = \{\{B, \ A\}, \{A, \ C\}, \{C, \ B\}, \{D, \ C\}\}$$</p><p><strong>Note:</strong> We use parentheses when it comes to directed edges, but curly braces with undirected edges as there is no direction from one vertex to the other.</p>
<p>When two vertices share an edge, they are <strong>adjacent</strong> to each other. The <strong>degree</strong> of a vertex is the number of adjacent vertices to it. We can also define the degree as the number of edges coming out of the vertex. For example, in the above image, the vertex A has a degree of 2.</p>
<p>A <strong>simple path</strong> is the one that we don't repeat any vertices while traversing the graph.</p>
<p>An example might look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747921569022/50f556e7-f954-4203-8c4b-493b2be5a353.gif" alt="Animated visualization of the same graph above with highlighted nodes in this order: A, B, C, D." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>A <strong>cycle</strong> is a simple path, except that we end up at the vertex we started with:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747921594728/717408ba-f9fb-4cef-9b52-70f487e9162d.gif" alt="Animated visualization of the same graph above with highlighted nodes in this order: A, B, C (with all the edges also highlighted)." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<h3 id="heading-representing-graphs">Representing graphs</h3>
<p>When it comes to representing graphs, there are several ways to do it, and we'll look at three of them: an edge list, an adjacency matrix, and an adjacency list.</p>
<h4 id="heading-edge-list">Edge List</h4>
<p>We can simply put all the edges in an array:</p>
<pre><code class="lang-plaintext">[ [A, B], [A, C], [B, C], [C, D] ]
</code></pre>
<p>But to find an edge in an edge list, we'll have to iterate through them, so it will have \(O(E)\) time complexity, where in the worst case, we'll search the whole list to find an edge. Similarly, it needs \(O(E)\) amount of space to represent all the edges.</p>
<h4 id="heading-adjacency-matrix">Adjacency Matrix</h4>
<p>The adjacency matrix for our example might look like this:</p>
<p>$$\left\lceil\begin{matrix}&amp; A &amp; B &amp; C &amp; D \\A &amp; 0 &amp; 1 &amp; 1 &amp; 0 \\B &amp; 1 &amp; 0 &amp; 1 &amp; 0 \\C &amp; 1 &amp; 1 &amp; 0 &amp; 1 \\D &amp; 0 &amp; 0 &amp; 1 &amp; 0\end{matrix}\right\rceil$$</p><p>Each row is for a vertex, and the matching column shows the relationship between those vertices. For example, the vertex A doesn't have an edge pointing to D, so the cell that matches A and D is 0. On the other hand, A is connected to B and C, so those cells have the value 1.</p>
<p><strong>Note:</strong> If the graph is weighted, we can simply put the weight instead of <code>1</code>, and when there is no edge, the value can stay <code>0</code>.</p>
<p><em>An adjacency matrix will have 0s in the "main diagonal," showing that there are no self-loops.</em></p>
<p>Let's try implementing it in TypeScript.</p>
<p>We'll start with a minimal graph vertex:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> GraphVertex {
  value: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">value: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span></span>) {
    <span class="hljs-built_in">this</span>.value = value;
  }
}
</code></pre>
<p>Now we can define our graph. We'll make it really simple with three properties to hold: <code>matrix</code> to represent the graph as an adjacency matrix, <code>vertices</code> to hold vertices, and <code>isDirected</code> to indicate whether our graph is directed:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> Graph {
  matrix: <span class="hljs-built_in">number</span>[][];
  vertices: GraphVertex[];
  isDirected: <span class="hljs-built_in">boolean</span>;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">vertices: GraphVertex[], isDirected = <span class="hljs-literal">true</span></span>) {
    <span class="hljs-built_in">this</span>.vertices = vertices;
    <span class="hljs-built_in">this</span>.isDirected = isDirected;
    <span class="hljs-comment">// ...</span>
  }

  <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Initializing our adjacency matrix might look like this:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.matrix = <span class="hljs-built_in">Array</span>.from({ length: vertices.length }, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">Array</span>.from({ length: vertices.length }, <span class="hljs-function">() =&gt;</span> <span class="hljs-number">0</span>)
});
</code></pre>
<p>We'll have an array with the length of vertices. Each item in the array is an array with the length of vertices as well, but filled with zeroes.</p>
<p>In our example with four vertices, the initial adjacency matrix looks like this:</p>
<pre><code class="lang-typescript">[ [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>] ]
</code></pre>
<p>Then, adding an edge is just marking the corresponding value as <code>1</code>, so that we can represent a connection between two vertices:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.matrix[<span class="hljs-built_in">this</span>.vertices.indexOf(v1)][<span class="hljs-built_in">this</span>.vertices.indexOf(v2)] = <span class="hljs-number">1</span>;
</code></pre>
<p><strong>Note:</strong> This implementation assumes that all vertices are distinct.</p>
<p>If we have an undirected graph, we can have it both ways:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.isDirected) {
  <span class="hljs-built_in">this</span>.matrix[<span class="hljs-built_in">this</span>.vertices.indexOf(v2)][<span class="hljs-built_in">this</span>.vertices.indexOf(v1)] = <span class="hljs-number">1</span>;
}
</code></pre>
<p>Removing an edge, in this case, will be just resetting the value to <code>0</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.matrix[<span class="hljs-built_in">this</span>.vertices.indexOf(v1)][<span class="hljs-built_in">this</span>.vertices.indexOf(v2)] = <span class="hljs-number">0</span>;
</code></pre>
<p>And, checking for the existence of an edge is simply checking whether the corresponding value is <code>0</code> or not:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.matrix[<span class="hljs-built_in">this</span>.vertices.indexOf(v1)][<span class="hljs-built_in">this</span>.vertices.indexOf(v2)] !== <span class="hljs-number">0</span>;
</code></pre>
<p>And, here is the whole example with additional methods for adding and removing an edge, checking if there is an edge between two vertices, and checking if a specific vertex is in the graph:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> Graph {
  matrix: <span class="hljs-built_in">number</span>[][];
  vertices: GraphVertex[];
  isDirected: <span class="hljs-built_in">boolean</span>;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">vertices: GraphVertex[], isDirected = <span class="hljs-literal">true</span></span>) {
    <span class="hljs-built_in">this</span>.vertices = vertices;
    <span class="hljs-built_in">this</span>.matrix = <span class="hljs-built_in">Array</span>.from({ length: vertices.length }, <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">Array</span>.from({ length: vertices.length }, <span class="hljs-function">() =&gt;</span> <span class="hljs-number">0</span>)
    });
    <span class="hljs-built_in">this</span>.isDirected = isDirected;
  }

  addEdge(v1: GraphVertex, v2: GraphVertex) {
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v1);
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v2);

    <span class="hljs-built_in">this</span>.matrix[<span class="hljs-built_in">this</span>.vertices.indexOf(v1)][<span class="hljs-built_in">this</span>.vertices.indexOf(v2)] = <span class="hljs-number">1</span>;

    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.isDirected) {
      <span class="hljs-built_in">this</span>.matrix[<span class="hljs-built_in">this</span>.vertices.indexOf(v2)][<span class="hljs-built_in">this</span>.vertices.indexOf(v1)] = <span class="hljs-number">1</span>;
    }
  }

  <span class="hljs-comment">/* 
  For a weighted graph:

  addEdge(v1: GraphVertex, v2: GraphVertex, weight: number) {
    this._checkVertexIsInGraph(v1);
    this._checkVertexIsInGraph(v2);

    this.matrix[this.vertices.indexOf(v1)][this.vertices.indexOf(v2)] = weight;
    if (!this.isDirected) {
      this.matrix[this.vertices.indexOf(v2)][this.vertices.indexOf(v1)] = weight;
    }
  }
  */</span>

  removeEdge(v1: GraphVertex, v2: GraphVertex) {
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v1);
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v2);

    <span class="hljs-built_in">this</span>.matrix[<span class="hljs-built_in">this</span>.vertices.indexOf(v1)][<span class="hljs-built_in">this</span>.vertices.indexOf(v2)] = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.isDirected) {
      <span class="hljs-built_in">this</span>.matrix[<span class="hljs-built_in">this</span>.vertices.indexOf(v2)][<span class="hljs-built_in">this</span>.vertices.indexOf(v1)] = <span class="hljs-number">0</span>;
    }
  }

  hasEdge(v1: GraphVertex, v2: GraphVertex) {
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v1);
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v2);

    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.matrix[<span class="hljs-built_in">this</span>.vertices.indexOf(v1)][<span class="hljs-built_in">this</span>.vertices.indexOf(v2)] !== <span class="hljs-number">0</span>;
  }

  getAdjacencyMatrix() {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.matrix;
  }

  _checkVertexIsInGraph(v: GraphVertex) {
    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.vertices.includes(v)) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Vertex doesn\'t exist'</span>);
    }
  }
}


<span class="hljs-keyword">let</span> a = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'A'</span>);
<span class="hljs-keyword">let</span> b = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'B'</span>);
<span class="hljs-keyword">let</span> c = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'C'</span>);
<span class="hljs-keyword">let</span> d = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'D'</span>);

<span class="hljs-keyword">let</span> graph = <span class="hljs-keyword">new</span> Graph([a, b, c, d], <span class="hljs-literal">false</span>);

graph.addEdge(a, b);
graph.addEdge(a, c);
graph.addEdge(b, c);
graph.addEdge(c, d);

<span class="hljs-built_in">console</span>.log(graph.getAdjacencyMatrix());
<span class="hljs-comment">// -&gt; [ [0, 1, 1, 0], [1, 0, 1, 0], [1, 1, 0, 1], [0, 0, 1, 0] ]</span>
</code></pre>
<p>Operations on an adjacency matrix have \(O(1)\) time complexity. But our storage needs will be \(O(V^2)\) where \(V\) is the number of vertices.</p>
<h4 id="heading-adjacency-list">Adjacency List</h4>
<p>In an adjacency list, usually a hashmap <strong>or</strong> an array of linked lists is used. For example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> graph = {
  <span class="hljs-string">'A'</span>: [<span class="hljs-string">'B'</span>, <span class="hljs-string">'C'</span>],
  <span class="hljs-string">'B'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'C'</span>],
  <span class="hljs-string">'C'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'B'</span>, <span class="hljs-string">'D'</span>],
  <span class="hljs-string">'D'</span>: [<span class="hljs-string">'C'</span>]
}
</code></pre>
<p>Let's see how we can modify our code above to use an adjacency list instead.</p>
<p>Instead of having a <code>matrix</code> which is an array of arrays, we can have a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map"><code>Map</code></a> that maps the vertices to an array of their neighbors.</p>
<p>We can initialize it as a map that has the vertices as keys, each of which has a value of an empty array for now:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.list = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>&lt;GraphVertex, GraphVertex[]&gt;();
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> v <span class="hljs-keyword">of</span> vertices) {
  <span class="hljs-built_in">this</span>.list.set(v, []);
}
</code></pre>
<p>Adding an edge will be just pushing to the array of corresponding vertex:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.list.get(v1)!.push(v2);
</code></pre>
<p>If our graph is undirected, we can do it both ways here as well:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.isDirected) {
  <span class="hljs-built_in">this</span>.list.get(v2)!.push(v1);
}
</code></pre>
<p>Removing an edge will be deleting that vertex from the array:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.list.set(v1, <span class="hljs-built_in">this</span>.list.get(v1)!.filter(<span class="hljs-function"><span class="hljs-params">v</span> =&gt;</span> v !== v2));
</code></pre>
<p>Checking if an edge exists is just checking the existence of that vertex in the array:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">this</span>.list.get(v1)!.includes(v2);
</code></pre>
<p><strong>Note:</strong> We're using a <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator">non-null assertion operator</a> as we’re using TypeScript in these examples. As we'll see below, we first check if the vertex is in the graph. And since we're adding all the vertices in the graph as keys to <code>this.list</code>, we're sure that getting that vertex from the list is not <code>undefined</code>. But TypeScript will warn us because if a key is not found in a <code>Map</code> object, it could <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get#return_value">potentially return <code>undefined</code></a>.</p>
<p>Here is our graph:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> Graph {
  list: <span class="hljs-built_in">Map</span>&lt;GraphVertex, GraphVertex[]&gt;;
  vertices: GraphVertex[];
  isDirected: <span class="hljs-built_in">boolean</span>;

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">vertices: GraphVertex[], isDirected = <span class="hljs-literal">true</span></span>) {
    <span class="hljs-built_in">this</span>.vertices = vertices;
    <span class="hljs-built_in">this</span>.list = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>();
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> v <span class="hljs-keyword">of</span> vertices) {
      <span class="hljs-built_in">this</span>.list.set(v, []);
    }
    <span class="hljs-built_in">this</span>.isDirected = isDirected;
  }

  addEdge(v1: GraphVertex, v2: GraphVertex) {
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v1);
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v2);

    <span class="hljs-built_in">this</span>.list.get(v1)!.push(v2);

    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.isDirected) {
      <span class="hljs-built_in">this</span>.list.get(v2)!.push(v1);
    }
  }

  removeEdge(v1: GraphVertex, v2: GraphVertex) {
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v1);
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v2);

    <span class="hljs-built_in">this</span>.list.set(v1, <span class="hljs-built_in">this</span>.list.get(v1)!.filter(<span class="hljs-function"><span class="hljs-params">v</span> =&gt;</span> v !== v2));

    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.isDirected) {
      <span class="hljs-built_in">this</span>.list.set(v2, <span class="hljs-built_in">this</span>.list.get(v2)!.filter(<span class="hljs-function"><span class="hljs-params">v</span> =&gt;</span> v !== v1));
    }
  }

  hasEdge(v1: GraphVertex, v2: GraphVertex) {
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v1);
    <span class="hljs-built_in">this</span>._checkVertexIsInGraph(v2);

    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.list.get(v1)!.includes(v2);
  }

  getAdjacencyList() {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.list;
  }

  _checkVertexIsInGraph(v: GraphVertex) {
    <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.vertices.includes(v)) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Vertex doesn\'t exist'</span>);
    }
  }
}


<span class="hljs-keyword">let</span> a = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'A'</span>);
<span class="hljs-keyword">let</span> b = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'B'</span>);
<span class="hljs-keyword">let</span> c = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'C'</span>);
<span class="hljs-keyword">let</span> d = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'D'</span>);

<span class="hljs-keyword">let</span> graph = <span class="hljs-keyword">new</span> Graph([a, b, c, d], <span class="hljs-literal">false</span>);

graph.addEdge(a, b);
graph.addEdge(a, c);
graph.addEdge(b, c);
graph.addEdge(c, d);

<span class="hljs-built_in">console</span>.log(graph.getAdjacencyList());

<span class="hljs-comment">/* Output:

Map (4) {
  GraphVertex: { "value": "A" } =&gt; [
    GraphVertex: { "value": "B" }, 
    GraphVertex: { "value": "C" }
  ], 
  GraphVertex: { "value": "B" } =&gt; [
    GraphVertex: { "value": "A" }, 
    GraphVertex: { "value": "C" }
  ], 
  GraphVertex: { "value": "C" } =&gt; [
    GraphVertex: { "value": "A" }, 
    GraphVertex: { "value": "B" }, 
    GraphVertex: { "value": "D" }
  ], 
  GraphVertex: { "value": "D" } =&gt; [
    GraphVertex: { "value": "C" }
  ]
} 

*/</span>
</code></pre>
<p>Getting the neighbors of a vertex is \(O(1)\) because we're just looking up a key in a map. But finding a particular edge can be \(O(d)\) where \(d\) is the number of degrees of the vertex, because we might need to traverse all the neighbors to find it. And, it could be \(V - 1\) where \(V\) is the number of vertices in the graph. It's the case when that vertex has all the other vertices as its neighbors.</p>
<p>The space complexity can be \(O(V + E)\) where \(V\) is the number of vertices and \(E\) is the number of edges.</p>
<h3 id="heading-traversals-1">Traversals</h3>
<p>Continuing with the adjacency list representation, let's now take a look at two (very familiar) ways to traverse a graph: breadth-first search and depth-first search.</p>
<p>But first, we'll modify our graph a little bit. We'll add a new vertex <code>'E'</code> and update some edges:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> a = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'A'</span>);
<span class="hljs-keyword">let</span> b = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'B'</span>);
<span class="hljs-keyword">let</span> c = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'C'</span>);
<span class="hljs-keyword">let</span> d = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'D'</span>);
<span class="hljs-keyword">let</span> e = <span class="hljs-keyword">new</span> GraphVertex(<span class="hljs-string">'E'</span>);


<span class="hljs-keyword">let</span> graph = <span class="hljs-keyword">new</span> Graph([a, b, c, d, e], <span class="hljs-literal">false</span>);

graph.addEdge(a, b);
graph.addEdge(a, c);
graph.addEdge(b, d);
graph.addEdge(c, e);
</code></pre>
<p>The important idea to remember is that there is no hierarchy of vertices, so we don't have a root node.</p>
<p>For a breadth-first or depth-first search, we can use an arbitrary node as a starting point.</p>
<h4 id="heading-breadth-first-search">Breadth-First Search</h4>
<p>With our new graph, a breadth-first search traversal looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747922399341/f4b9f63b-5188-48a2-83ec-51524721c2b1.gif" alt="Animated visualization for a breadth-first search of a graph with nodes A, B, C, D, E with highlighted nodes in this order: A, B, C, D, E." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>When it comes to breadth-first search, usually a queue is used, and the idea is simple: given a current node, we'll add the adjacent nodes first, marking them as visited as we go.</p>
<p>Inside the <code>Graph</code> class, we can implement a <code>bfs</code> method that does just that:</p>
<pre><code class="lang-typescript">bfs(startNode: GraphVertex) {
  <span class="hljs-keyword">const</span> visited = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>();
  <span class="hljs-keyword">const</span> queue = [startNode];
  visited.add(startNode);

  <span class="hljs-keyword">while</span> (queue.length &gt; <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">const</span> currentNode = queue.shift();
    <span class="hljs-comment">// console.log(currentNode);</span>
    <span class="hljs-built_in">this</span>.list.get(currentNode <span class="hljs-keyword">as</span> GraphVertex)!.forEach(<span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (!visited.has(node)) {
        visited.add(node);
        queue.push(node);
      }
    });
  }
}
</code></pre>
<p>If we call the <code>bfs</code> method with <code>a</code> as the starting vertex (<code>graph.bfs(a)</code>), and log <code>currentNode</code> to console each time we go, it's as we expected:</p>
<pre><code class="lang-plaintext">GraphVertex { value: 'A' }
GraphVertex { value: 'B' }
GraphVertex { value: 'C' }
GraphVertex { value: 'D' }
GraphVertex { value: 'E' }
</code></pre>
<p>With the adjacency list, using a BFS has \(O(V + E)\) time complexity (sum of the vertices and edges) as we're traversing the whole graph.</p>
<h4 id="heading-depth-first-search">Depth-First Search</h4>
<p>With the same modified graph, a depth-first search looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747922463260/72e852c1-642f-4ce0-829d-e658a8a7b880.gif" alt="Animated visualization for a depth-first search of a graph with nodes A, B, C, D, E with highlighted nodes in this order: A, B, D, C, E." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>With depth-first search there is usually recursion involved as we're traversing through a path until we have visited all the nodes in that path. Once we hit a dead end, we'll <strong>backtrack</strong> and continue exploring until we have visited all the vertices in the graph.</p>
<p>Again, inside the <code>Graph</code> class, we can add a <code>dfs</code> method:</p>
<pre><code class="lang-typescript">dfs(startNode: GraphVertex, visited = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>()) {
  visited.add(startNode);
  <span class="hljs-comment">// console.log(startNode);</span>
  <span class="hljs-built_in">this</span>.list.get(startNode)!.forEach(<span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (!visited.has(node)) {
      <span class="hljs-built_in">this</span>.dfs(node, visited);
    }
  });
}
</code></pre>
<p>Starting with a node, we check how deep we can go from there. Once we reach a dead end (when the <code>dfs</code> inside <code>forEach</code> returns), we continue checking other neighbors (with <code>forEach</code>) until none is left. We essentially do the same thing until all the vertices are visited.</p>
<p>Logging the output matches our expectation:</p>
<pre><code class="lang-plaintext">GraphVertex { value: 'A' }
GraphVertex { value: 'B' }
GraphVertex { value: 'D' }
GraphVertex { value: 'C' }
GraphVertex { value: 'E' }
</code></pre>
<p>The time complexity for a depth-first search traversal of a graph is the similar to BFS, \(O(V + E)\).</p>
<h2 id="heading-chapter-twelve-dynamic-programming">Chapter Twelve: Dynamic Programming</h2>
<p>Dynamic programming (DP) is one of those concepts that is a bit intimidating when you hear it for the first time. But the crux of it is simply breaking problems down into smaller parts and solving them. It’s also about storing those solutions so that we don't have to compute them again.</p>
<p>Breaking problems down into subproblems is nothing new – that's pretty much what problem-solving is all about. What dynamic programming is also specifically concerned with are <strong>overlapping subproblems</strong> that are repeating – we want to calculate solutions to those subproblems so that we won't be calculating them again each time. Put another way, <em>we want to remember the past so that we won't be condemned to repeat it</em>.</p>
<p>For example, calculating 1 + 1 + 1 + 1 + 1 is very easy if we have already calculated 1 + 1 + 1 + 1. We can just remember the previous solution, and use it:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747922611072/bcb33cae-aed3-41bd-8823-4e10a0e13fbf.gif" alt="Animated visualization of the result of 1 + 1 + 1 + 1 constituting a subproblem of 1 + 1 + 1 + 1 + 1 ." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>Calculating the Fibonacci sequence is one of the well-known examples when it comes to dynamic programming. Because we have to calculate the same functions each time for a new number, it lends itself to DP very well.</p>
<p>For example, to calculate <code>fib(4)</code> we need to calculate <code>fib(3)</code> and <code>fib(2)</code>. But calculating <code>fib(3)</code> also involves calculating <code>fib(2)</code>, so we'll be doing the same calculation, <em>again</em>.</p>
<p>A classic, good old recursive Fibonacci function might look like this:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fib</span>(<span class="hljs-params">n: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">if</span> (n === <span class="hljs-number">0</span> || n === <span class="hljs-number">1</span>) {
    <span class="hljs-keyword">return</span> n;
  }

  <span class="hljs-keyword">return</span> fib(n - <span class="hljs-number">1</span>) + fib(n - <span class="hljs-number">2</span>);
}
</code></pre>
<p>Though the issue we have just mentioned remains: we'll keep calculating the same values:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747922659848/577aef57-17b5-40ad-a926-74387b0e3731.gif" alt="Animated visualization displaying repeated fibonacci calls." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>But, we want to do better.</p>
<p><strong>Memoization</strong> is remembering the problems we have solved before so that we don't have to solve them again and waste our time. We can <em>reuse</em> the solution to the subproblem we've already memoized. So, we can keep a <em>cache</em> to store those solutions and use them:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fib</span>(<span class="hljs-params">n: <span class="hljs-built_in">number</span>, cache: <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">number</span>, <span class="hljs-built_in">number</span>&gt;</span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">if</span> (cache.has(n)) {
    <span class="hljs-keyword">return</span> cache.get(n)!;
  }

  <span class="hljs-keyword">if</span> (n === <span class="hljs-number">0</span> || n === <span class="hljs-number">1</span>) {
    <span class="hljs-keyword">return</span> n;
  }

  <span class="hljs-keyword">const</span> result = fib(n - <span class="hljs-number">1</span>, cache) + fib(n - <span class="hljs-number">2</span>, cache);
  cache.set(n, result);

  <span class="hljs-keyword">return</span> result;
}
</code></pre>
<p>For example, we can initially pass an empty <code>Map</code> as the argument for <code>cache</code>, and print the first 15 Fibonacci numbers:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> m = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">number</span>, <span class="hljs-built_in">number</span>&gt;();

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">15</span>; i++) {
  <span class="hljs-built_in">console</span>.log(fib(i, m));
}

<span class="hljs-comment">/*
  0
  1
  1
  2
  3
  5
  8
  13
  21
  34
  55 
  89
  144
  233
  377 
 */</span>
</code></pre>
<p>There are two different approaches with dynamic programming: <strong>top-down</strong> and <strong>bottom-up</strong>.</p>
<p>Top-down is like what it sounds: starting with a large problem, breaking it down to smaller components, memoizing them. It's what we just did with the <code>fib</code> example.</p>
<p>Bottom-up is also like what it sounds: starting with the smallest subproblem, finding out a solution, and working our way up to the larger problem itself. It also has an advantage: with the bottom-up approach, we don't need to store every previous value – we can only keep the two elements at the bottom so that we can use them to build up to our target.</p>
<p>With the bottom-up approach, our <code>fib</code> function can look like this:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fib</span>(<span class="hljs-params">n: <span class="hljs-built_in">number</span></span>) </span>{
  <span class="hljs-keyword">let</span> dp = [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>];
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">2</span>; i &lt;= n; i++) {
    dp[i] = dp[i - <span class="hljs-number">1</span>] + dp[i - <span class="hljs-number">2</span>];
  }

  <span class="hljs-keyword">return</span> dp[n];
}
</code></pre>
<p>Just note that we are keeping an array whose size will grow linearly as the input increases. So, we can do better with constant space complexity, not using an array at all:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fib</span>(<span class="hljs-params">n: <span class="hljs-built_in">number</span></span>) </span>{
  <span class="hljs-keyword">if</span> (n === <span class="hljs-number">0</span> || n === <span class="hljs-number">1</span>) {
    <span class="hljs-keyword">return</span> n;
  }

  <span class="hljs-keyword">let</span> a = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">let</span> b = <span class="hljs-number">1</span>;

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">2</span>; i &lt;= n; i++) {
    <span class="hljs-keyword">let</span> tmp = a + b;
    a = b;
    b = tmp;
  }

  <span class="hljs-keyword">return</span> b;
}
</code></pre>
<h4 id="heading-time-and-space-complexity-13">Time and space complexity</h4>
<p>The time complexities for both the top-down and bottom-up approaches in the Fibonacci example are \(O(n)\) as we solve each subproblem, each of which is of constant time.</p>
<p><strong>Note:</strong> The time complexity of the recursive Fibonacci function that doesn't use DP is exponential (in fact, \(O(\phi^{n})\) – yes <a target="_blank" href="https://en.wikipedia.org/wiki/Golden_ratio">the golden ratio</a> as its base).</p>
<p>But when it comes to space complexity, the bottom-up approach (the second version) is \(O(1)\).</p>
<p><strong>Note:</strong> The first version we've used for the bottom-up approach has \(O(n)\) time complexity as we store the values in an array.</p>
<p>The top-down approach has \(O(n)\) space complexity because we store a <code>Map</code> whose size will grow linearly as <code>n</code> increases.</p>
<h2 id="heading-chapter-thirteen-intervals">Chapter Thirteen: Intervals</h2>
<p>An interval simply has a start and an end. The easiest way to think about intervals is as time frames.</p>
<p>With intervals, the usual concern is whether they overlap or not.</p>
<p>For example, if we have an interval <code>[1, 3]</code> and another <code>[2, 5]</code>, they are clearly overlapping, so they can be merged together to create a new interval <code>[1, 5]</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747923034510/05767713-f24f-4467-82f5-89e025631be9.gif" alt="Animated visualization with interval [1, 3] merging with [2, 5], becoming [1, 5]." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<p>In order for two intervals <strong>not to overlap</strong>:</p>
<ul>
<li>the <em>start</em> of one should be <em>strictly larger</em> than the <em>end</em> of the other</li>
</ul>
<pre><code class="lang-plaintext">newInterval[0] &gt; interval[1]
</code></pre>
<p>Or:</p>
<ul>
<li>the <em>end</em> of the one should be <em>strictly smaller</em> than the <em>start</em> of the other</li>
</ul>
<pre><code class="lang-plaintext">newInterval[1] &lt; interval[0]
</code></pre>
<p>If both of these are false, they are overlapping.</p>
<p>If they are overlapping, the new (merged) interval will have the minimum value from both intervals as its start, and the maximum as its end:</p>
<pre><code class="lang-plaintext">[
  min(newInterval[0], interval[0]),
  max(newInterval[1], interval[1])
]
</code></pre>
<h2 id="heading-chapter-fourteen-bit-manipulation">Chapter Fourteen: Bit Manipulation</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Bitwise_operation">A bitwise operation</a> operates on a bit string, a bit array, or a binary numeral (considered as a bit string) at the level of its individual bits.</p>
<p>Let's first represent a number in binary (base 2). We can use <code>toString</code> method on a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a>, and specify the <strong>radix</strong>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> n = <span class="hljs-number">17</span>;

<span class="hljs-built_in">console</span>.log(n.toString(<span class="hljs-number">2</span>)); <span class="hljs-comment">// 10001</span>
</code></pre>
<p>We can also parse an integer giving it a base:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">parseInt</span>(<span class="hljs-number">10001</span>, <span class="hljs-number">2</span>)); <span class="hljs-comment">// 17</span>
</code></pre>
<p><strong>Note:</strong> We can also represent a binary number with the prefix <code>0b</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(<span class="hljs-number">0b10001</span>); <span class="hljs-comment">// 17</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-number">0b101</span>); <span class="hljs-comment">// 5</span>
</code></pre>
<p>For example, these are the same number:</p>
<pre><code class="lang-javascript"><span class="hljs-number">0b1</span> === <span class="hljs-number">0b00000001</span> <span class="hljs-comment">// true</span>
</code></pre>
<p>All bitwise operations are performed on 32-bit binary numbers in JavaScript. That is, <em>before a bitwise operation is performed, JavaScript converts numbers to 32-bit</em> <strong><em>signed</em></strong> <em>integers.</em></p>
<p>So, for example, <code>17</code> won't be simply <code>10001</code> but <code>00000000 00000000 00000000 00010001</code>.</p>
<p><em>After the bitwise operation is performed, the result is converted back to 64-bit JavaScript numbers.</em></p>
<h3 id="heading-bitwise-operators">Bitwise operators</h3>
<h4 id="heading-and-amp">AND (<code>&amp;</code>)</h4>
<p>If two bits are <code>1</code>, the result is <code>1</code>, otherwise <code>0</code>.</p>
<p>The GIFs below show the numbers as 8-bit strings, but when doing bitwise operations, remember they are converted to 32-bit numbers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747923324409/4415a01f-6129-4dcf-a1ce-b8e898bdba9a.gif" alt="Animated visualization of an AND operation. 00010001 &amp; 00000101 = 00000001." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> x1 = <span class="hljs-number">0b10001</span>;
<span class="hljs-keyword">const</span> x2 = <span class="hljs-number">0b101</span>;

<span class="hljs-keyword">const</span> result = x1 &amp; x2; <span class="hljs-comment">// 1 (0b1)</span>
</code></pre>
<h4 id="heading-or">OR (<code>|</code>)</h4>
<p>If either of the bits is <code>1</code>, the result is <code>1</code>, otherwise <code>0</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747923365941/22e562c4-e195-4567-b8ef-4b94cb4a394f.gif" alt="Animated visualization of an OR operation. 00010001 | 00000101 = 00010101." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> x1 = <span class="hljs-number">0b10001</span>;
<span class="hljs-keyword">const</span> x2 = <span class="hljs-number">0b101</span>;

<span class="hljs-keyword">const</span> result = x1 | x2; <span class="hljs-comment">// 21 (0b10101)</span>
</code></pre>
<h4 id="heading-xor">XOR (<code>^</code>)</h4>
<p>If the bits are different (one is <code>1</code> and the other is <code>0</code>), the result is <code>1</code>, otherwise <code>0</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747923403151/52090e1a-98ee-4303-a433-6318ec6f18d2.gif" alt="Animated visualization of a XOR operation. 00010001 ^ 00000101 = 00010100." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> x1 = <span class="hljs-number">0b10001</span>;
<span class="hljs-keyword">const</span> x2 = <span class="hljs-number">0b101</span>;

<span class="hljs-keyword">const</span> result = x1 ^ x2; <span class="hljs-comment">// 20 (0b10100)</span>
</code></pre>
<h4 id="heading-not">NOT (<code>~</code>)</h4>
<p>Flips the bits (<code>1</code> becomes <code>0</code>, <code>0</code> becomes <code>1</code>).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747923458432/2807b821-8069-45e6-a37b-d8167ec722e7.gif" alt="Animated visualization of a NOT operation. ~00010001 = 11101110." class="image--center mx-auto" width="1080" height="608" loading="lazy"></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> n = <span class="hljs-number">17</span>;

<span class="hljs-keyword">const</span> result = ~n; <span class="hljs-comment">// -18</span>
</code></pre>
<p><strong>Note:</strong> Bitwise NOTing any 32-bit integer <code>x</code> yields <code>-(x + 1)</code>.</p>
<p>If we use <a target="_blank" href="https://stackoverflow.com/a/33758875">a helper function</a> to see the binary representations, it is as we expected:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(createBinaryString(n));
<span class="hljs-comment">// -&gt; 00000000 00000000 00000000 00010001</span>

<span class="hljs-built_in">console</span>.log(createBinaryString(result));
<span class="hljs-comment">// -&gt; 11111111 11111111 11111111 11101110</span>
</code></pre>
<p>The leftmost bit indicates the signal – whether the number is negative or positive.</p>
<p>Remember that we said JavaScript uses 32-bit <strong>signed</strong> integers for bitwise operations. <strong>The leftmost bit is</strong> <code>1</code> for negative numbers and <code>0</code> for positive numbers. Also, the operator operates on the operands' bit representations in <a target="_blank" href="https://en.wikipedia.org/wiki/Two's_complement">two's complement</a>. The operator is applied to each bit, and the result is constructed bitwise.</p>
<p><strong>Note:</strong> Two's complement allows us to get a number with an inverse signal. One way to do it is to invert the bits of the number in the positive representation and add 1 to it:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">twosComplement</span>(<span class="hljs-params">n</span>) </span>{
  <span class="hljs-keyword">return</span> ~n + <span class="hljs-number">0b1</span>;
}
</code></pre>
<h4 id="heading-left-shift-zero-fill-ltlt">Left shift (zero fill) (<code>&lt;&lt;</code>)</h4>
<p>Shifts the given number of bits to the left, adding zero bits shifted in from the right.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> n = <span class="hljs-number">17</span>;
<span class="hljs-keyword">const</span> result = n &lt;&lt; <span class="hljs-number">1</span>; <span class="hljs-comment">// 34</span>


<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">17</span>));
<span class="hljs-comment">// -&gt; 00000000 00000000 00000000 00010001</span>

<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">34</span>));
<span class="hljs-comment">// -&gt; 00000000 00000000 00000000 00100010</span>
</code></pre>
<p><strong>Note</strong> that the 32nd bit (the leftmost one) is discarded.</p>
<h4 id="heading-right-shift-sign-preserving-gtgt">Right shift (sign preserving) (<code>&gt;&gt;</code>)</h4>
<p>Shifts the given number of bits to the right, preserving the sign when adding bits from the left.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> n = <span class="hljs-number">17</span>;
<span class="hljs-keyword">const</span> result = n &gt;&gt; <span class="hljs-number">1</span>; <span class="hljs-comment">// 8</span>


<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">17</span>));
<span class="hljs-comment">// -&gt; 00000000 00000000 00000000 00010001</span>

<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">8</span>));
<span class="hljs-comment">// -&gt; 00000000 00000000 00000000 00001000</span>
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> n = <span class="hljs-number">-17</span>;
<span class="hljs-keyword">const</span> result = n &gt;&gt; <span class="hljs-number">1</span>; <span class="hljs-comment">// -9</span>

<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">-17</span>));
<span class="hljs-comment">// -&gt; 11111111 11111111 11111111 11101111</span>

<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">-9</span>));
<span class="hljs-comment">// -&gt; 11111111 11111111 11111111 11110111</span>
</code></pre>
<h4 id="heading-right-shift-unsigned-gtgtgt">Right shift (unsigned) (<code>&gt;&gt;&gt;</code>)</h4>
<p>Shifts the given number of bits to the right, adding <code>0</code>s when adding bits in from the left, no matter what the sign is.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> n = <span class="hljs-number">17</span>;
<span class="hljs-keyword">const</span> result = n &gt;&gt;&gt; <span class="hljs-number">1</span>; <span class="hljs-comment">// 8</span>


<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">17</span>));
<span class="hljs-comment">// -&gt; 00000000 00000000 00000000 00010001</span>

<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">8</span>));
<span class="hljs-comment">// -&gt; 00000000 00000000 00000000 00001000</span>
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> n = <span class="hljs-number">-17</span>;
<span class="hljs-keyword">const</span> result = n &gt;&gt;&gt; <span class="hljs-number">1</span>; <span class="hljs-comment">// 2147483639</span>

<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">-17</span>));
<span class="hljs-comment">// -&gt; 11111111 11111111 11111111 11101111</span>

<span class="hljs-built_in">console</span>.log(createBinaryString(<span class="hljs-number">2147483639</span>));
<span class="hljs-comment">// -&gt; 01111111 11111111 11111111 11110111</span>
</code></pre>
<h3 id="heading-getting-a-bit">Getting a bit</h3>
<p>To get a specific bit, we first need to create a <strong>bitmask</strong>. We can do this by shifting <code>1</code> to the left by the index of the bit we want to get. The result is the <strong>AND</strong> of the binary number and the bitmask.</p>
<p>But using JavaScript, we can also do an unsigned right shift by the index to put the bit in the first place (so that we don't get the actual value that is in that position, but whether it is a <code>1</code> or a <code>0</code>):</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getBit</span>(<span class="hljs-params">number, idx</span>) </span>{
  <span class="hljs-keyword">const</span> bitMask = <span class="hljs-number">1</span> &lt;&lt; idx;
  <span class="hljs-keyword">const</span> result = number &amp; bitMask;

  <span class="hljs-keyword">return</span> result &gt;&gt;&gt; idx;
}
</code></pre>
<p>For example, let's try <code>13</code>, which is <code>1101</code> in binary:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> binaryNumber = <span class="hljs-number">0b1101</span>;

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Bit at position 0:'</span>, getBit(binaryNumber, <span class="hljs-number">0</span>));
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Bit at position 1:'</span>, getBit(binaryNumber, <span class="hljs-number">1</span>));
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Bit at position 2:'</span>, getBit(binaryNumber, <span class="hljs-number">2</span>));
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Bit at position 3:'</span>, getBit(binaryNumber, <span class="hljs-number">3</span>));

<span class="hljs-comment">/*
Output:

Bit at position 0: 1
Bit at position 1: 0
Bit at position 2: 1
Bit at position 3: 1
*/</span>
</code></pre>
<h3 id="heading-setting-a-bit">Setting a bit</h3>
<p>If we want to turn a bit to <code>1</code> (in other words, to "<em>set a bit</em>"), we can do a similar thing.</p>
<p>First, we can create a bitmask again by shifting <code>1</code> to the left by the index of the bit we want to set to <code>1</code>. The result is the <strong>OR</strong> of the number and the bitmask:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setBit</span>(<span class="hljs-params">number, idx</span>) </span>{
  <span class="hljs-keyword">const</span> bitMask = <span class="hljs-number">1</span> &lt;&lt; idx;
  <span class="hljs-keyword">return</span> number | bitMask;    
}
</code></pre>
<p>Remember that in our example <code>13</code> was <code>1101</code> in binary, let's say we want to set the <code>0</code> at index 1:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> binaryNumber = <span class="hljs-number">0b1101</span>;
<span class="hljs-keyword">const</span> newBinaryNumber = setBit(binaryNumber, <span class="hljs-number">1</span>);

<span class="hljs-built_in">console</span>.log(createBinaryString(newBinaryNumber));
<span class="hljs-comment">// -&gt; 00000000 00000000 00000000 00001111</span>

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Bit at position 1:'</span>, getBit(newBinaryNumber, <span class="hljs-number">1</span>));
<span class="hljs-comment">// -&gt; Bit at position 1: 1</span>
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>With some detours here and there, we took a look at fourteen (or fifteen, if you count our <em>interlude</em>) different concepts, from arrays and hashing to bit manipulation.</p>
<p>Although I have to say that eventually, with time, it’s easy to forget all that we learned. But, that's not a problem in itself, because as you might have realized, if there is one idea that should stick with you with this handbook, it’s that problems are best solved when they are broken into smaller parts. And, as with anything else, writing or talking to yourself (see <a target="_blank" href="https://www.freecodecamp.org/news/rubber-duck-debugging/">duck debugging</a>) works miracles.</p>
<p>Now, it's time to take a deep breath.</p>
<p>It was a delightful adventure to explore data structures and algorithms, and hopefully you found some value in it.</p>
<p>Have a beautiful journey ahead, and until then, happy coding.</p>
<h3 id="heading-resources-amp-credits">Resources &amp; Credits</h3>
<p>This handbook was mainly inspired by the amazing <a target="_blank" href="https://medium.com/basecs">BaseCS series</a> by Vaidehi Joshi, which is an incredible resource for learning basic computer science concepts.</p>
<p>The visualization idea was inspired by Lydia Hallie's <a target="_blank" href="https://dev.to/lydiahallie/series/3341">JavaScript Visualized series</a>.</p>
<p>Of course, you can also check out <a target="_blank" href="https://neetcode.io/courses">NeetCode's courses</a> which can be incredibly helpful for a serious study.</p>
<p>There are many other resources to check out if you want to go further, here are some of the ones I used in our exploration:</p>
<ul>
<li><p><a target="_blank" href="http://brilliant.org">brilliant.org</a></p>
</li>
<li><p><a target="_blank" href="http://leetcodethehardway.com">leetcodethehardway.com</a></p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference">MDN Web Docs</a></p>
</li>
<li><p><a target="_blank" href="http://baeldung.com/cs">baeldung.com/cs</a></p>
</li>
<li><p><a target="_blank" href="https://lucasfcosta.com/2018/12/25/bitwise-operations.html"><em>The Absolute Essentials for Bit Manipulation in JavaScript</em> by Lucas F. Costa</a></p>
</li>
<li><p><a target="_blank" href="https://condor.depaul.edu/ntomuro/courses/402/notes/heap.html">Heap &amp; HeapSort - Noriko Tomuro</a></p>
</li>
<li><p><a target="_blank" href="https://faculty.cs.niu.edu/~freedman/340/340notes/340heap.htm">Heaps - Professor Reva Freedman</a></p>
</li>
<li><p><a target="_blank" href="https://realpython.com/how-to-implement-python-stack/#using-collectionsdeque-to-create-a-python-stack">"Using <code>collections.deque</code> to Create a Python Stack" - Jim Anderson</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Recursive Types in TypeScript: A Brief Exploration ]]>
                </title>
                <description>
                    <![CDATA[ It is said that there are two different worlds in TypeScript that exist side by side: the type world and the value world. Consider this line of code: const firstName: string = 'Maynard'; While firstName and 'Maynard' live in the value world, string ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/recursive-types-in-typescript-a-brief-exploration/</link>
                <guid isPermaLink="false">681b77a47396438eaee0c5ce</guid>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Eda Eren ]]>
                </dc:creator>
                <pubDate>Wed, 07 May 2025 15:09:24 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746625684891/6a4e2dae-a7b2-415d-a65d-be47c5d73253.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://www.totaltypescript.com/books/total-typescript-essentials/the-weird-parts#the-type-and-value-worlds">It is said</a> that there are two different worlds in TypeScript that exist side by side: the type world and the value world.</p>
<p>Consider this line of code:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> firstName: <span class="hljs-built_in">string</span> = <span class="hljs-string">'Maynard'</span>;
</code></pre>
<p>While <code>firstName</code> and <code>'Maynard'</code> live in the value world, <code>string</code> belongs to the type world.</p>
<p>Or, consider the <code>typeof</code> operator that exists in both worlds.</p>
<p>Here it is in the value world:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">typeof</span> <span class="hljs-number">42</span>); <span class="hljs-comment">// number</span>
</code></pre>
<p>And, here it is in the type world, used for extracting the type of the function <code>increment</code>, which is then passed to <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype">a utility type called <code>ReturnType</code></a>:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">increment</span>(<span class="hljs-params">n: <span class="hljs-built_in">number</span></span>) </span>{
  <span class="hljs-keyword">return</span> n + <span class="hljs-number">1</span>;
}

<span class="hljs-keyword">type</span> T = ReturnType&lt;<span class="hljs-keyword">typeof</span> increment&gt;; <span class="hljs-comment">// number</span>
</code></pre>
<p>One of the wonders of the type world is the existence of recursive types – types that refer to themselves. They are somewhat similar to recursive functions that you might already be familiar with.</p>
<p>Here is a recursive function:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addUpTo</span>(<span class="hljs-params">n: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> </span>{
  <span class="hljs-keyword">if</span> (n === <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">return</span> n;
  }

  <span class="hljs-keyword">return</span> n + addUpTo(n - <span class="hljs-number">1</span>);
}
</code></pre>
<p>And, here is a recursive type:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> JSONValue =
  | <span class="hljs-built_in">string</span>
  | <span class="hljs-built_in">number</span>
  | <span class="hljs-built_in">boolean</span>
  | <span class="hljs-literal">null</span>
  | { [key: <span class="hljs-built_in">string</span>]: JSONValue };
</code></pre>
<p>The function <code>addUpTo</code> calls itself, each time with a lesser value of <code>n</code>, gradually reaching the base case. (<code>n</code> is assumed to be a nonnegative number, otherwise, we'll have the <code>Maximum call stack size exceeded</code> error.)</p>
<p>The type <code>JSONValue</code> is a union type that has an <em>index signature</em> in one of its possible types (<code>{ [key: string]: JSONValue }</code>), and the value is a reference to itself.</p>
<p>To see it with an example, let's say we have an object <code>person</code> that has the properties <code>name</code>, <code>age</code>, and <code>friends</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> person = {
  name: <span class="hljs-string">'Alice'</span>,
  age: <span class="hljs-number">25</span>,
  friends: {
    <span class="hljs-number">0</span>: {
      name: <span class="hljs-string">'Bob'</span>,
      age: <span class="hljs-number">23</span>,
      friends: {
        <span class="hljs-comment">// ...</span>
      }
    },
    <span class="hljs-number">1</span>: {
      name: <span class="hljs-string">'Carol'</span>,
      age: <span class="hljs-number">28</span>,
      friends: {
        <span class="hljs-comment">// ...</span>
      }
    },
    <span class="hljs-comment">// ...</span>
  }
};
</code></pre>
<p>The value of <code>friends</code> is an object that has numbers as its properties, each having the value that looks just like the person we've just defined. So, we can create a <code>Person</code> type for it where the value of <code>friends</code> is an object that also has the value type <code>Person</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Person = {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
  friends: {
    [key: <span class="hljs-built_in">number</span>]: Person;
  };
};
</code></pre>
<p><strong>Note</strong>: In JavaScript, object keys are either strings or symbols. Even if we define <code>key</code> as a <code>number</code>, JavaScript eventually will coerce the object keys to strings.</p>
<p>In this article, we'll not only take a look at what recursive types are in TypeScript, but we’ll also see how they can apply to recursive data structures and how they can be useful with two different use cases.</p>
<p>It can be an excellent tool in your toolkit for very specific circumstances such as when you need to extend a utility type or get the "inner" type of a multidimensional array.</p>
<p>Let's begin our exploration.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-recursive-types-for-trees-and-linked-lists">Recursive Types for Trees and Linked Lists</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-recursion-with-mapped-and-conditional-types">Recursion with Mapped and Conditional Types</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-cases-for-recursive-types">Use Cases for Recursive Types</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-use-case-1-deeppartial">Use Case 1: DeepPartial</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-use-case-2-unwraparray">Use Case 2: UnwrapArray</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion-and-a-warning">Conclusion (and a Warning!)</a></p>
</li>
</ol>
<h2 id="heading-recursive-types-for-trees-and-linked-lists">Recursive Types for Trees and Linked Lists</h2>
<p>Recursive types are probably best understood with a data structure like a tree:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746454664280/0eed3e38-151e-403f-8d88-028519e23f3e.png" alt="A binary tree where the root node has two children, left and right. The left child has left and right children, each of them also has left and right children of their own. The right child of the root node has a left child that has a left child of its own, and a right child that is a leaf node." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>For example, a node of a binary tree has at most two children, left and right. A child node, if it also has children, is the root of a subtree itself.</p>
<p>We can create a <code>TreeNode</code> type for a binary tree:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> TreeNode&lt;T&gt; = {
  value: T;
  left: TreeNode&lt;T&gt; | <span class="hljs-literal">null</span>;
  right: TreeNode&lt;T&gt; | <span class="hljs-literal">null</span>;
};
</code></pre>
<p>It is a generic type, so the <code>value</code> can be any type that we pass to it. The <code>left</code> and <code>right</code> children can be either <code>TreeNode</code> themselves or <code>null</code>.</p>
<p>Let's say we have this binary tree:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746456573014/d748d7fb-1893-44af-9e37-bffcfaffec5d.png" alt="A binary tree that has the value 8 as its root node. It has a left child with the value 3 and a right child with the value 10. The left child has a left child node that has the value 1 and a right child node that has the value 6, which has a left child node with the value 4 and a right child node with the value 7. The right child of the root node that has the value 10 has a right child node with value 14, which has a left child node that has the value 13." class="image--right mx-auto mr-0" width="600" height="400" loading="lazy"></p>
<p>We can represent it just like this, with the type of <code>TreeNode</code> that we've just defined:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> binaryTree: TreeNode&lt;<span class="hljs-built_in">number</span>&gt; = {
  value: <span class="hljs-number">8</span>,
  left: {
    value: <span class="hljs-number">3</span>,
    left: {
      value: <span class="hljs-number">1</span>,
      left: <span class="hljs-literal">null</span>,
      right: <span class="hljs-literal">null</span>
    },
    right: {
      value: <span class="hljs-number">6</span>,
      left: {
        value: <span class="hljs-number">4</span>,
        left: <span class="hljs-literal">null</span>,
        right: <span class="hljs-literal">null</span>
      },
      right: {
        value: <span class="hljs-number">7</span>,
        left: <span class="hljs-literal">null</span>,
        right: <span class="hljs-literal">null</span>
      }
    }
  },
  right: {
    value: <span class="hljs-number">10</span>,
    left: <span class="hljs-literal">null</span>,
    right: {
      value: <span class="hljs-number">14</span>,
      left: {
        value: <span class="hljs-number">13</span>,
        left: <span class="hljs-literal">null</span>,
        right: <span class="hljs-literal">null</span>
      },
      right: <span class="hljs-literal">null</span>
    }
  }
};
</code></pre>
<p>Since this is a generic type, we’re passing to it the type <code>number</code> that’s used as the type of <code>value</code>.</p>
<p>Similarly, we can create a type for a linked list where each node has a <code>value</code> and a <code>next</code> property that either points to another node or <code>null</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> LinkedList&lt;T&gt; = {
  value: T;
  next: LinkedList&lt;T&gt; | <span class="hljs-literal">null</span>;
};
</code></pre>
<p>So, if our linked list looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746456962646/9996894c-162a-4c71-b2b3-3c9369592551.jpeg" alt="A linked list where the head points to the node with the value 1, which points to the node with the value 2, which points to the node with the value 3 that points to null." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>We can represent it like this, with the type <code>LinkedList</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> linkedList: LinkedList&lt;<span class="hljs-built_in">number</span>&gt; = {
  value: <span class="hljs-number">1</span>,
  next: {
    value: <span class="hljs-number">2</span>,
    next: {
      value: <span class="hljs-number">3</span>,
      next: <span class="hljs-literal">null</span>
    }
  }
};
</code></pre>
<p><strong>Note</strong>: We used type aliases in the examples above, but we can also use an <code>interface</code> instead:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> TreeNode&lt;T&gt; {
  value: T;
  left: TreeNode&lt;T&gt; | <span class="hljs-literal">null</span>;
  right: TreeNode&lt;T&gt; | <span class="hljs-literal">null</span>;
}

<span class="hljs-keyword">interface</span> LinkedList&lt;T&gt; {
  value: T;
  next: LinkedList&lt;T&gt; | <span class="hljs-literal">null</span>;
}
</code></pre>
<h2 id="heading-recursion-with-mapped-and-conditional-types">Recursion with Mapped and Conditional Types</h2>
<p>Applying recursive types to values representing recursive data structures is a bit obvious and not too exciting, but we can explore other options where recursion can also be used, such as mapped and conditional types.</p>
<p>Mapped types are a convenient way to create a new type based on another one. We can, for example, create a new type using the keys of an object, where we <em>map</em> the keys to a different value type.</p>
<p>Let’s say we have a <code>colors</code> object that has color names and the corresponding hex values. The values are of <code>string</code> type, but let’s say we want to create an object type where the keys are the same, except that the type of the values should be <code>boolean</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> colors = {
  aquamarine: <span class="hljs-string">'#7fffd4'</span>,
  black: <span class="hljs-string">'#000000'</span>,
  blueviolet: <span class="hljs-string">'#8a2be2'</span>,
  goldenrod: <span class="hljs-string">'#daa520'</span>,
  indigo: <span class="hljs-string">'#4b0082'</span>,
  lavender: <span class="hljs-string">'#e6e6fa'</span>,
  silver: <span class="hljs-string">'#c0c0c0'</span>
};

<span class="hljs-keyword">type</span> ColorsToBoolean&lt;T&gt; = {
  [K <span class="hljs-keyword">in</span> keyof T]: <span class="hljs-built_in">boolean</span>;
};

<span class="hljs-keyword">type</span> Result = ColorsToBoolean&lt;<span class="hljs-keyword">typeof</span> colors&gt;;
</code></pre>
<p>The <code>Result</code> type then will look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746481133106/390515bd-4f98-4072-a8f8-94a3ef39cec4.png" alt="A screenshot of the code block that's defined above when it's hovered over the type `Result`. Keys are the same as the ones in the `colors` object, and all the values are `boolean`." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In order to create a <em>recursive</em> mapped type, however, we need a reference to the same type that we’re creating, like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Recursive&lt;T&gt; = {
  [K <span class="hljs-keyword">in</span> keyof T]: Recursive&lt;T[K]&gt;;
};
</code></pre>
<p>Before going further, let's also take a look at the conditional types, which look very similar to the conditional expressions that use <a target="_blank" href="https://www.freecodecamp.org/news/javascript-ternary-operator-explained/">the ternary operator</a>:</p>
<pre><code class="lang-typescript">AType <span class="hljs-keyword">extends</span> AnotherType ? ResultTypeIfTrue : ResultTypeIfFalse;
</code></pre>
<p>It has this familiar form:</p>
<pre><code class="lang-plaintext">condition ? resultIfTrue : resultIfFalse
</code></pre>
<p>Now, we can combine both mapped and conditional types to create a recursive mapped conditional type:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Recursive&lt;T&gt; = {
  [K <span class="hljs-keyword">in</span> keyof T]: T[K] <span class="hljs-keyword">extends</span> <span class="hljs-built_in">number</span> ? T[K] : Recursive&lt;T[K]&gt;;
};
</code></pre>
<p>We map the keys of the given object type to the same value type if it’s a <code>number</code>, otherwise, continue with the recursion.</p>
<h2 id="heading-use-cases-for-recursive-types">Use Cases for Recursive Types</h2>
<h3 id="heading-use-case-1-deeppartial">Use Case 1: <code>DeepPartial</code></h3>
<p>One use case of recursive types is extending the capabilities of the utility type <code>Partial</code>.</p>
<p>Let's say we have a type for an article, using an <code>interface</code> this time:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> IArticle {
  title: <span class="hljs-built_in">string</span>;
  description: <span class="hljs-built_in">string</span>;
  url: <span class="hljs-built_in">string</span>;
  author: {
    name: <span class="hljs-built_in">string</span>;
    age: <span class="hljs-built_in">number</span>;
  };
}
</code></pre>
<p><code>Partial</code> makes all the properties of an object type that it's given optional.</p>
<p>But, if we try to do this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> article: Partial&lt;IArticle&gt; = {
  title: <span class="hljs-string">'Navigating the Mysteries'</span>,
  description:
    <span class="hljs-string">'As we walk our questions into a troubled future, storyteller and mythologist Martin Shaw invites us to subvert today’s voices of certainty and do the hard work of opening to mystery.'</span>,
  url: <span class="hljs-string">'https://emergencemagazine.org/essay/navigating-the-mysteries'</span>,
  author: {
    name: <span class="hljs-string">'Martin Shaw'</span>
  }
};
</code></pre>
<p>We'll have an error: <code>Property 'age' is missing in type '{ name: string; }' but required in type '{ name: string; age: number; }'</code>. All the properties are optional as expected, except for <code>age</code> which is a property of the property <code>author</code>. So, <code>Partial</code> is not going to work for objects with more than one level of depth.</p>
<p>In our example, we don't want to pass in an <code>age</code> property for the <code>author</code>, so we need to find a way to make it work.</p>
<p>In fact, the recursive mapped conditional type that we've just defined above is a perfect use case for this. We can use recursion so that all the properties are optional, no matter their depth:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> DeepPartial&lt;T&gt; = {
  [K <span class="hljs-keyword">in</span> keyof T]?: T[K] <span class="hljs-keyword">extends</span> <span class="hljs-built_in">object</span> ? DeepPartial&lt;T[K]&gt; : T[K];
};
</code></pre>
<p>Now, if we try our example with <code>DeepPartial</code>, there are no errors, and the problem is resolved:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> article: DeepPartial&lt;IArticle&gt; = {
  title: <span class="hljs-string">'Navigating the Mysteries'</span>,
  description:
    <span class="hljs-string">'As we walk our questions into a troubled future, storyteller and mythologist Martin Shaw invites us to subvert today’s voices of certainty and do the hard work of opening to mystery.'</span>,
  author: {
    name: <span class="hljs-string">'Martin Shaw'</span>
  }
};
</code></pre>
<h3 id="heading-use-case-2-unwraparray">Use Case 2: <code>UnwrapArray</code></h3>
<p>Another use case we can take a look at is when we need to have the “inner“ type of a multidimensional array.</p>
<p>Consider this one:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> UnwrapArray&lt;A&gt; = A <span class="hljs-keyword">extends</span> <span class="hljs-built_in">Array</span>&lt;infer T&gt; ? UnwrapArray&lt;T&gt; : A;
</code></pre>
<p>We define an <code>UnwrapArray</code> generic type. If the type we pass to it is yet another array (<code>A extends Array</code>), then it's passed to <code>UnwrapArray</code> again until we reach a type that doesn't extend <code>Array</code>.</p>
<p><strong>Note</strong> that we use the <code>infer</code> keyword to extract the type. <code>infer</code> is only used with conditional types when <code>extends</code> is used, so it's perfect for our purpose here.</p>
<p>Now, we can get the inner type:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Result = UnwrapArray&lt;<span class="hljs-built_in">string</span>[][][]&gt;; <span class="hljs-comment">// string</span>
</code></pre>
<p>Using the keyword <code>Array</code> will have the same result:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Result = UnwrapArray&lt;<span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">string</span>&gt;&gt;&gt;&gt;; <span class="hljs-comment">// string</span>
</code></pre>
<h2 id="heading-conclusion-and-a-warning">Conclusion (and a Warning!)</h2>
<p>The universe of recursive types in TypeScript is fascinating and very powerful. But, of course, with great power comes great responsibility. TypeScript's own documentation warns us:</p>
<blockquote>
<p>Keep in mind that while these recursive types are powerful, they should be used responsibly and sparingly. <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html#recursive-conditional-types">(Source)</a></p>
</blockquote>
<p>It’s not only that recursive types can result in longer time for type-checking, but with enough complexity, it can also result in a compile-time error. In fact, the documentation also tells us not to use them at all if possible.</p>
<p>So, was all this learning for nothing?</p>
<p>The answer depends on what you make of it. Recursion is a powerful concept that definitely has use cases in TypeScript as we've seen in this article, and if used responsibly, it can be an excellent tool.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
