<?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" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Harsh Jaiswal on Medium]]></title>
        <description><![CDATA[Stories by Harsh Jaiswal on Medium]]></description>
        <link>https://medium.com/@harshjaiswal2707?source=rss-cebfcd2e3977------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Gh1KGFBSNm0xeEe5X3k1Dw.jpeg</url>
            <title>Stories by Harsh Jaiswal on Medium</title>
            <link>https://medium.com/@harshjaiswal2707?source=rss-cebfcd2e3977------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 04 Nov 2026 23:42:04 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@harshjaiswal2707/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[ Understanding Trie (Prefix Tree) — From Fundamentals to LeetCode Problems]]></title>
            <link>https://medium.com/@harshjaiswal2707/understanding-trie-prefix-tree-from-fundamentals-to-leetcode-problems-ea7f7873b692?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/ea7f7873b692</guid>
            <category><![CDATA[enjoyproblem]]></category>
            <category><![CDATA[leetcode]]></category>
            <category><![CDATA[sad]]></category>
            <category><![CDATA[interviewproblem]]></category>
            <category><![CDATA[try]]></category>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Tue, 03 Nov 2026 02:05:27 GMT</pubDate>
            <atom:updated>2026-11-03T02:05:27.950Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction</h3><p>While learning Data Structures and Algorithms, I recently started revising <strong>Trie (Prefix Tree)</strong>, a tree-based data structure mainly used for storing and searching strings efficiently.</p><p>Unlike arrays, linked lists, or binary search trees, Trie is specifically designed to handle strings and their prefixes.</p><p>For example, consider these words:</p><ul><li>apple</li><li>app</li><li>application</li><li>apt</li><li>bat</li></ul><p>Instead of storing these words independently, Trie stores their characters in a tree structure, allowing words with common prefixes to share the same path.</p><p>This makes Trie particularly useful in applications such as:</p><ul><li>Autocomplete and search suggestions.</li><li>Dictionary implementations.</li><li>Prefix-based searching.</li><li>Spell checking.</li><li>Word searching.</li></ul><p>Let’s understand how it works.</p><h3>1. What is Trie?</h3><p>A <strong>Trie (Prefix Tree)</strong> is a tree-based data structure in which each node represents a character, and the path from the root to a node represents a prefix or a complete word.</p><p>The important idea behind Trie is that words having common prefixes share the same nodes.</p><p>For example, consider:</p><p>apple, app, apt, bat</p><p>Their Trie representation looks like this:</p><pre>                  Root<br>                 /    \<br>                a      b<br>                |      |<br>                p      a<br>               / \     |<br>              p   t    t<br>              |<br>              l<br>              |<br>              e</pre><p>Here:</p><ul><li>apple and app share the prefix app.</li><li>app and apt share the prefix ap.</li><li>bat follows a separate path.</li><li>Each word-ending node is marked using a boolean variable called isEnd.</li></ul><p><strong>Important:</strong> Reaching a node does not necessarily mean that a complete word exists. We need isEnd = true to identify a complete word.</p><h3>2. Trie Node Structure</h3><p>In a standard Trie implementation for lowercase English letters, each node contains:</p><ol><li>children[26]: An array of pointers representing the 26 possible lowercase English characters.</li><li>isEnd: A boolean variable indicating whether a complete word ends at that node.</li></ol><pre>class Node {<br>public:<br>    Node* children[26];<br>    bool isEnd;</pre><pre>    Node() {<br>        for(int i = 0; i &lt; 26; i++) {<br>            children[i] = NULL;<br>        }</pre><pre>        isEnd = false;<br>    }<br>};</pre><p><strong>Why do we use children[26]?</strong></p><p>We map every lowercase English character to an index:</p><pre>int idx = ch - &#39;a&#39;;</pre><p>For example:</p><ul><li>a → 0</li><li>b → 1</li><li>c → 2</li><li>z → 25</li></ul><p>This allows us to access the next character directly in O(1) time.</p><h3>3. Important Trie Operations</h3><p>There are three fundamental operations that we should understand.</p><h3>A. Insert()</h3><p>The insert operation adds a word into the Trie.</p><p><strong>Approach:</strong></p><ol><li>Start from the root node.</li><li>Traverse every character of the word.</li><li>Calculate its index using ch - &#39;a&#39;.</li><li>If the corresponding child does not exist, create a new node.</li><li>Move to that child.</li><li>After processing all characters, mark the current node as isEnd = true.</li></ol><pre>void insert(string word) {<br>    Node* curr = root;</pre><pre>    for(char ch : word) {<br>        int idx = ch - &#39;a&#39;;</pre><pre>        if(curr-&gt;children[idx] == NULL) {<br>            curr-&gt;children[idx] = new Node();<br>        }</pre><pre>        curr = curr-&gt;children[idx];<br>    }</pre><pre>    curr-&gt;isEnd = true;<br>}</pre><h3>B. Search()</h3><p>The search operation checks whether a complete word exists in the Trie.</p><p><strong>Approach:</strong></p><ol><li>Start from the root.</li><li>Traverse each character.</li><li>If any required child does not exist, return false.</li><li>Otherwise, continue moving through the Trie.</li><li>After processing the complete word, check isEnd.</li></ol><pre>bool search(string word) {<br>    Node* curr = root;</pre><pre>    for(char ch : word) {<br>        int idx = ch - &#39;a&#39;;</pre><pre>        if(curr-&gt;children[idx] == NULL) {<br>            return false;<br>        }</pre><pre>        curr = curr-&gt;children[idx];<br>    }</pre><pre>    return curr-&gt;isEnd;<br>}</pre><p><strong>Important:</strong> We check isEnd because a prefix can exist without being a complete word.</p><h3>C. StartsWith()</h3><p>This operation checks whether any word in the Trie starts with a given prefix.</p><p>Unlike search, it does not require the last node to have isEnd = true.</p><pre>bool startsWith(string prefix) {<br>    Node* curr = root;</pre><pre>    for(char ch : prefix) {<br>        int idx = ch - &#39;a&#39;;</pre><pre>        if(curr-&gt;children[idx] == NULL) {<br>            return false;<br>        }</pre><pre>        curr = curr-&gt;children[idx];<br>    }</pre><pre>    return true;<br>}</pre><h3>4. Time and Space Complexity</h3><p>Let L represent the length of the given word or prefix.</p><p>OperationTime ComplexityInsertO(L)SearchO(L)StartsWithO(L)</p><p><strong>Space Complexity:</strong> O(N × L) in the worst case, where N is the number of inserted words and L is their maximum length, assuming a fixed 26-character alphabet.</p><p>Trie is efficient for prefix-based operations because we only traverse the characters of the given string instead of checking every stored word.</p><h3>5. LeetCode Problems I Solved</h3><p>After understanding the basic Trie implementation, I applied the concept to three LeetCode problems. Each problem introduced a different application of Trie.</p><h3>Problem 1: LC 208 — Implement Trie (Prefix Tree)</h3><p><strong>Difficulty:</strong> Medium</p><p><strong>What does the problem ask?</strong></p><p>We need to design a Trie data structure that supports three operations:</p><ol><li>insert(word): Insert a word into the Trie.</li><li>search(word): Check whether the complete word exists in the Trie.</li><li>startsWith(prefix): Check whether any previously inserted word starts with the given prefix.</li></ol><p>For example:</p><pre>Trie trie;</pre><pre>trie.insert(&quot;apple&quot;);</pre><pre>trie.search(&quot;apple&quot;);     // true<br>trie.search(&quot;app&quot;);       // false<br>trie.startsWith(&quot;app&quot;);   // true</pre><pre>trie.insert(&quot;app&quot;);</pre><pre>trie.search(&quot;app&quot;);       // true</pre><p>Notice the difference between search() and startsWith().</p><p>Although &quot;app&quot; is a prefix of &quot;apple&quot;, it is not initially a complete word in the Trie. Therefore, search(&quot;app&quot;) returns false, while startsWith(&quot;app&quot;) returns true.</p><p><strong>Approach: Basic Trie Implementation</strong></p><p>I implemented a custom Node class containing:</p><ul><li>An array of 26 child pointers.</li><li>A boolean isEnd to identify the end of a complete word.</li></ul><p>I maintained a root pointer and implemented all three operations using iterative traversal.</p><p>The insertion operation creates nodes when they do not exist, while searching follows the existing character paths.</p><p><strong>Key Learning:</strong> Understanding the Trie node structure, character mapping, and the importance of the isEnd flag.</p><p><a href="https://leetcode.com/problems/implement-trie-prefix-tree/">LeetCode 208 — Implement Trie</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-qaGfK9W6upz_xXYkyd8xg.png" /></figure><h3>Problem 2: LC 211 — Design Add and Search Words Data Structure</h3><p><strong>Difficulty:</strong> Medium</p><p><strong>What does the problem ask?</strong></p><p>We need to design a data structure that supports two operations:</p><ol><li>addWord(word): Add a word to the data structure.</li><li>search(word): Check whether a word exists in the data structure.</li></ol><p>However, there is an additional condition: the search operation supports the . character as a wildcard.</p><p>The . character can represent any single character.</p><p>For example:</p><pre>addWord(&quot;bad&quot;);<br>addWord(&quot;dad&quot;);<br>addWord(&quot;mad&quot;);</pre><pre>search(&quot;pad&quot;);  // false<br>search(&quot;bad&quot;);  // true<br>search(&quot;.ad&quot;);  // true<br>search(&quot;b..&quot;);  // true</pre><p>Here:</p><ul><li>&quot;pad&quot; returns false because no matching word exists.</li><li>&quot;bad&quot; returns true because the exact word exists.</li><li>&quot;.ad&quot; returns true because . can represent b, d, or m.</li><li>&quot;b..&quot; returns true because the two dots can represent a and d.</li></ul><p><strong>Approach: Trie + DFS + Backtracking</strong></p><p>The insertion operation works exactly like the basic Trie implementation in LC 208.</p><p>The main challenge is handling the wildcard character during searching.</p><p>We handle two cases:</p><p><strong>Case 1: Current character is not </strong><strong>.</strong></p><ul><li>Calculate the character index.</li><li>Check whether the corresponding child exists.</li><li>If it does not exist, return false.</li><li>Otherwise, recursively search the next character.</li></ul><p><strong>Case 2: Current character is </strong><strong>.</strong></p><p>Since the dot can represent any character, we cannot follow just one child.</p><p>Instead, we explore all existing children using DFS.</p><p>If any recursive path successfully matches the remaining characters, we return true.</p><p>Once we process the entire search word, we check whether the current node has isEnd = true.</p><p><strong>Key Learning:</strong> This problem taught me how to combine Trie with DFS and Backtracking to handle wildcard-based searching.</p><p><a href="https://leetcode.com/problems/design-add-and-search-words-data-structure/">LeetCode 211 — Design Add and Search Words Data Structure</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*C8orieN1fIJ-R3-Z7KMo2w.png" /></figure><h3>Problem 3: LC 1268 — Search Suggestions System</h3><p><strong>Difficulty:</strong> Medium</p><p><strong>What does the problem ask?</strong></p><p>We are given:</p><ul><li>An array of product names.</li><li>A string called searchWord.</li></ul><p>We need to build a search suggestion system.</p><p>As the user types each character of the search word, we must display up to three product suggestions that match the current prefix.</p><p>The suggestions must be in lexicographical (dictionary) order.</p><p>For example:</p><pre>products = {<br>    &quot;mobile&quot;,<br>    &quot;mouse&quot;,<br>    &quot;moneypot&quot;,<br>    &quot;monitor&quot;,<br>    &quot;mousepad&quot;<br>};</pre><pre>searchWord = &quot;mouse&quot;;</pre><p>The expected output is:</p><pre>[<br>    [&quot;mobile&quot;, &quot;moneypot&quot;, &quot;monitor&quot;],<br>    [&quot;mobile&quot;, &quot;moneypot&quot;, &quot;monitor&quot;],<br>    [&quot;mouse&quot;, &quot;mousepad&quot;],<br>    [&quot;mouse&quot;, &quot;mousepad&quot;],<br>    [&quot;mouse&quot;, &quot;mousepad&quot;]<br>]</pre><p><strong>Approach: Trie + Sorting + Suggestions</strong></p><p>I followed these steps:</p><ol><li>Sort all products lexicographically.</li><li>Insert every product into the Trie.</li><li>Each Trie node maintains a list of at most three suggestions.</li><li>During insertion, add the current product to the suggestion list of every node along its path, provided the list contains fewer than three products.</li><li>Since products are inserted in sorted order, the first three stored products are already the lexicographically smallest suggestions.</li><li>Traverse the Trie according to the search word, character by character.</li><li>After reaching each prefix node, return its stored suggestions.</li></ol><p>If a character path does not exist, return an empty list for that prefix and all remaining prefixes.</p><p><strong>Key Learning:</strong> This problem demonstrated how Trie can be extended to build an autocomplete system by storing useful information at every prefix node.</p><p><a href="https://leetcode.com/problems/search-suggestions-system/">LeetCode 1268 — Search Suggestions System</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7MCKzPdV77tcgk1OW9eGOQ.png" /></figure><h3>Final Takeaway</h3><p>These three problems helped me understand Trie from three different perspectives:</p><ul><li><strong>LC 208:</strong> How to implement and operate a Trie.</li><li><strong>LC 211:</strong> How to handle wildcard characters using DFS and Backtracking.</li><li><strong>LC 1268:</strong> How to use Trie for real-world autocomplete and search suggestions.</li></ul><p>This revision reinforced an important DSA lesson: learning a data structure is not just about understanding its implementation, but also about recognizing how to adapt it to solve different problems.</p><h3>6. What I Learned from These Three Problems</h3><p>These three problems helped me understand how the same Trie data structure can be adapted to solve different problems.</p><p>The interesting part is that the underlying Trie structure remains almost the same, but the searching logic changes according to the problem requirements.</p><h3>Conclusion</h3><p>Trie is one of the important data structures for string and prefix-based problems.</p><p>Initially, understanding the node structure, child pointers, and isEnd flag is essential. Once these fundamentals are clear, we can extend Trie with DFS, Backtracking, Sorting, and other techniques to solve more complex problems.</p><p>My biggest takeaway from revising Trie is that <strong>understanding the structure is only the first step; learning how to adapt it to different problem requirements is what makes it powerful.</strong></p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#Tries #Leetcode #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ea7f7873b692" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Hashing in DSA: From Fundamentals to Advanced Problem-Solving Patterns]]></title>
            <link>https://medium.com/@harshjaiswal2707/hashing-in-dsa-from-fundamentals-to-advanced-problem-solving-patterns-defd0b66f599?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/defd0b66f599</guid>
            <category><![CDATA[interviewproblem]]></category>
            <category><![CDATA[leetcode]]></category>
            <category><![CDATA[sad]]></category>
            <category><![CDATA[hashing]]></category>
            <category><![CDATA[enjoyproblem]]></category>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Sat, 31 Oct 2026 02:15:28 GMT</pubDate>
            <atom:updated>2026-10-31T02:15:28.336Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction</h3><p>While revising Data Structures and Algorithms, I decided to focus on Hashing and understand how different hash-based data structures work.</p><p>Before this revision, I had already encountered hash maps and sets in several DSA problems. However, I wanted to understand them more deeply, especially the differences between ordered and unordered containers, their internal working, and when to use each one.</p><p>During this revision, I explored four important problems:</p><ul><li><a href="https://leetcode.com/problems/longest-consecutive-sequence/">LeetCode 128 — Longest Consecutive Sequence</a></li><li><a href="https://leetcode.com/problems/subarray-sum-equals-k/">LeetCode 560 — Subarray Sum Equals K</a></li><li><a href="https://leetcode.com/problems/find-all-anagrams-in-a-string/">LeetCode 438 — Find All Anagrams in a String</a></li><li><a href="https://leetcode.com/problems/lru-cache/">LeetCode 146 — LRU Cache</a></li></ul><p>These problems demonstrate how hashing can optimize solutions and reduce unnecessary operations.</p><p>Let’s understand everything step by step.</p><h3>1. What Is Hashing?</h3><p>Hashing is a technique used to store and retrieve data efficiently.</p><p>It uses a <strong>hash function</strong> to convert a key into an index (or hash value), which helps determine where the corresponding data should be stored.</p><p>Consider a simple example:</p><p>Suppose we have the following values:</p><p>10, 20, 30, 40, 50</p><p>If we want to check whether a particular value exists, searching through an array requires checking elements one by one.</p><p>In the worst case, this takes O(n) time.</p><p>With hashing, we can calculate a location using a hash function and access the corresponding data much faster.</p><p>For example:</p><pre>hash(10) = 10 % 10 = 0<br>hash(20) = 20 % 10 = 0<br>hash(30) = 30 % 10 = 0</pre><p>This is a simplified hash function, but it demonstrates an important concept.</p><h3>How Does Hashing Work Internally?</h3><p>Hashing generally involves three important components:</p><p><strong>1. Key</strong></p><p>The value that we want to store or search.</p><p><strong>2. Hash Function</strong></p><p>A function that converts the key into a hash value.</p><p><strong>3. Bucket</strong></p><p>A storage location in the hash table where elements are organized.</p><p>The hash function determines which bucket should be used for a particular key.</p><h3>What Is a Hash Collision?</h3><p>A collision occurs when two different keys generate the same hash value.</p><p>For example:</p><pre>hash(10) = 10 % 10 = 0<br>hash(20) = 20 % 10 = 0</pre><p>Both values map to bucket 0.</p><p>This is called a hash collision.</p><p>Hash tables handle collisions using techniques such as:</p><ul><li><strong>Separate Chaining:</strong> Multiple elements can be stored in the same bucket, commonly using linked structures.</li><li><strong>Open Addressing:</strong> Another available position is searched when a collision occurs.</li></ul><p>C++ unordered containers use hash tables and implement collision handling internally.</p><h3>What Is a Load Factor?</h3><p>Load factor tells us how full a hash table is.</p><p>Formula:</p><p>Load Factor = Number of Elements / Number of Buckets</p><p>For example:</p><p>If a hash table contains 8 elements and 10 buckets:</p><p>Load Factor = 8 / 10 = 0.8</p><p>When the load factor becomes too high, the hash table may rehash.</p><p><strong>Rehashing</strong> means increasing or changing the bucket arrangement and redistributing existing elements.</p><p>This helps maintain efficient operations.</p><h3>2. Ordered vs Unordered Containers in C++</h3><p>C++ provides four important STL containers that are useful when solving hashing-related problems.</p><h3>A. map</h3><p>map stores data in sorted order based on its keys.</p><p>Internally, it is generally implemented using a Red-Black Tree (a self-balancing binary search tree).</p><p>Example:</p><pre>map&lt;int, string&gt; mp;</pre><pre>mp[3] = &quot;Three&quot;;<br>mp[1] = &quot;One&quot;;<br>mp[2] = &quot;Two&quot;;</pre><p>Output:</p><pre>1 One<br>2 Two<br>3 Three</pre><p>Even though we inserted 3, 1, and 2, the keys are automatically sorted.</p><p><strong>Time Complexity:</strong></p><p>OperationComplexity</p><p>Insert O(log n)</p><p>Search O(log n)</p><p>Delete O(log n)</p><p>Access by key O(log n)</p><h3>B. unordered_map</h3><p>unordered_map stores key-value pairs without maintaining sorted order.</p><p>It uses a hash table internally.</p><p>Example:</p><pre>unordered_map&lt;int, string&gt; mp;</pre><pre>mp[3] = &quot;Three&quot;;<br>mp[1] = &quot;One&quot;;<br>mp[2] = &quot;Two&quot;;</pre><p>The iteration order is not guaranteed to be sorted.</p><p><strong>Time Complexity:</strong></p><p>Operation = Average : Worst</p><p>Insert = O(1) : O(n)</p><p>Search = O(1) : O(n)</p><p>Delete = O(1) : O(n)</p><p>The average O(1) complexity makes unordered_map very useful in DSA problems.</p><h3>C. set</h3><p>set stores unique elements in sorted order.</p><p>Example:</p><pre>set&lt;int&gt; st;</pre><pre>st.insert(30);<br>st.insert(10);<br>st.insert(20);<br>st.insert(10);</pre><p>Output:</p><pre>10 20 30</pre><p>Duplicate elements are automatically removed.</p><p><strong>Time Complexity:</strong> Insert, search, and delete take O(log n).</p><h3>D. unordered_set</h3><p>unordered_set stores unique elements without maintaining sorted order.</p><p>Example:</p><pre>unordered_set&lt;int&gt; st;</pre><pre>st.insert(30);<br>st.insert(10);<br>st.insert(20);<br>st.insert(10);</pre><p>It stores only three unique elements.</p><p><strong>Time Complexity:</strong></p><ul><li>Insert: Average O(1)</li><li>Search: Average O(1)</li><li>Delete: Average O(1)</li></ul><p><strong>My understanding:</strong></p><ul><li>Use map when sorted keys are required.</li><li>Use unordered_map when fast key-value lookup is required.</li><li>Use set when unique sorted elements are required.</li><li>Use unordered_set when unique elements and fast lookup are required.</li></ul><h3>3. Important STL Functions</h3><p>Before moving to the problems, let’s understand some frequently used functions.</p><h3>unordered_map / map</h3><pre>unordered_map&lt;int, int&gt; mp;</pre><pre>// Insert or update<br>mp[10] = 5;</pre><pre>// Insert without overwriting an existing key<br>mp.insert({20, 10});</pre><pre>// Search<br>mp.find(10);</pre><pre>// Check existence<br>mp.count(10);</pre><pre>// Check whether key exists (C++20)<br>mp.contains(10);</pre><pre>// Remove<br>mp.erase(10);</pre><pre>// Number of elements<br>mp.size();</pre><pre>// Check whether empty<br>mp.empty();</pre><pre>// Remove everything<br>mp.clear();</pre><p><strong>Important difference:</strong></p><pre>mp[10]++;</pre><p>If key 10 does not exist, it is automatically created with a default value of 0 and then incremented.</p><p>Whereas:</p><pre>mp.find(10);</pre><p>Only searches for the key. It does not insert anything.</p><h3>set / unordered_set</h3><pre>unordered_set&lt;int&gt; st;</pre><pre>// Insert<br>st.insert(10);</pre><pre>// Search<br>st.find(10);</pre><pre>// Check existence<br>st.count(10);</pre><pre>// Check existence (C++20)<br>st.contains(10);</pre><pre>// Remove<br>st.erase(10);</pre><pre>// Number of elements<br>st.size();</pre><pre>// Check empty<br>st.empty();</pre><pre>// Remove all elements<br>st.clear();</pre><h3>Traversing Containers</h3><pre>for(auto it : mp) {<br>    cout &lt;&lt; it.first &lt;&lt; &quot; &quot; &lt;&lt; it.second &lt;&lt; endl;<br>}</pre><p>For a set:</p><pre>for(auto it : st) {<br>    cout &lt;&lt; it &lt;&lt; &quot; &quot;;<br>}</pre><p>One important point: map and set maintain sorted order, whereas unordered containers do not guarantee any particular iteration order.</p><h3>4. LeetCode 128 — Longest Consecutive Sequence</h3><p><strong>Problem:</strong> Given an unsorted array of integers, find the length of the longest consecutive elements sequence.</p><p><a href="https://leetcode.com/problems/longest-consecutive-sequence/">Problem Link</a></p><h3>Example</h3><pre>Input: nums = [100, 4, 200, 1, 3, 2]</pre><pre>Output: 4</pre><p>Explanation:</p><p>The longest consecutive sequence is:</p><p>1 → 2 → 3 → 4</p><p>Therefore, the answer is 4.</p><h3>Approach: Using unordered_set</h3><p>A straightforward approach would be sorting the array.</p><p>However, sorting takes O(n log n) time.</p><p>We can optimize this using unordered_set.</p><p><strong>Step 1:</strong> Insert all elements into an unordered_set.</p><p>This removes duplicates and provides average O(1) lookup.</p><p><strong>Step 2:</strong> Iterate through the set.</p><p>For every number, check whether its previous number (num - 1) exists.</p><ul><li>If it exists, the current number is not the beginning of a sequence.</li><li>If it doesn’t exist, we have found the starting point of a sequence.</li></ul><p><strong>Step 3:</strong> Starting from that number, keep checking whether the next consecutive number exists.</p><p><strong>Step 4:</strong> Update the maximum sequence length.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f2Tm_XngtJbQXJB-SOSp6A.png" /></figure><h3>Dry Run</h3><p>Input:</p><p>[100, 4, 200, 1, 3, 2]</p><p>After inserting elements:</p><p>{100, 4, 200, 1, 3, 2}</p><p>NumberPrevious Exists?Action100NoSequence length = 14Yes (3)Skip200NoSequence length = 11NoCheck 2, 3, 43Yes (2)Skip2Yes (1)Skip</p><p>The longest sequence is:</p><p>1 → 2 → 3 → 4</p><p>Answer = 4.</p><p><strong>Time Complexity:</strong> O(n) average.</p><p><strong>Space Complexity:</strong> O(n).</p><p>The important optimization is that we only start counting when we find the beginning of a sequence.</p><h3>5. LeetCode 560 — Subarray Sum Equals K</h3><p><strong>Problem:</strong> Given an integer array and an integer k, return the total number of continuous subarrays whose sum equals k.</p><p><a href="https://leetcode.com/problems/subarray-sum-equals-k/">Problem Link</a></p><h3>Example</h3><pre>Input: nums = [1, 1, 1], k = 2</pre><pre>Output: 2</pre><p>Explanation:</p><p>There are two subarrays:</p><p>[1, 1] and [1, 1]</p><p>Both have a sum equal to 2.</p><h3>Approach: Prefix Sum + unordered_map</h3><p>This problem introduces an important hashing pattern: <strong>Prefix Sum + Hash Map</strong>.</p><p>First, understand prefix sum.</p><p>A prefix sum represents the sum of elements from the beginning of an array up to the current index.</p><p>For example:</p><pre>nums = [1, 2, 3, 4]</pre><pre>Prefix Sum = [1, 3, 6, 10]</pre><p>Suppose:</p><p>prefixSum = 6</p><p>We want to find a previous prefix sum such that:</p><p>prefixSum - previousPrefixSum = k</p><p>Rearranging:</p><p>previousPrefixSum = prefixSum - k</p><p>Therefore, whenever we encounter a prefix sum that has a previous occurrence of prefixSum - k, we have found a subarray with sum k.</p><p>This is where unordered_map helps us.</p><h3>Algorithm</h3><ol><li>Initialize prefixSum = 0 and count = 0.</li><li>Create an unordered_map to store prefix sums and their frequencies.</li><li>Insert mp[0] = 1 to handle subarrays starting from index 0.</li><li>Traverse the array and update the prefix sum.</li><li>Check whether prefixSum - k exists in the map.</li><li>If it exists, add its frequency to the answer.</li><li>Store the current prefix sum frequency.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tMmpOFVYkvUGbpa5t_a7cA.png" /></figure><h3>Dry Run</h3><p>Input:</p><pre>nums = [1, 1, 1]<br>k = 2</pre><p>Initially:</p><pre>mp = {0: 1}<br>prefixSum = 0<br>count = 0</pre><p>ElementPrefix SumprefixSum — kCount11–1012011312</p><p>Final Answer = 2.</p><h3>Why Do We Initialize mp[0] = 1?</h3><p>This is one of the most important concepts in this problem.</p><p>Consider:</p><pre>nums = [2, 3]<br>k = 5</pre><p>When we reach the second element:</p><p>prefixSum = 5</p><p>Now:</p><p>prefixSum - k = 5 - 5 = 0</p><p>If we initialize mp[0] = 1, we can count the entire subarray [2, 3].</p><p>Without this initialization, we would miss subarrays starting from index 0.</p><p><strong>Time Complexity:</strong> O(n) average.</p><p><strong>Space Complexity:</strong> O(n).</p><p>The key learning is that a hash map can store the frequency of previous prefix sums, allowing us to count subarrays efficiently.</p><h3>6. LeetCode 438 — Find All Anagrams in a String</h3><p><strong>Problem:</strong> Given two strings s and p, return all the starting indices of p’s anagrams in s.</p><p><a href="https://leetcode.com/problems/find-all-anagrams-in-a-string/">Problem Link</a></p><h3>Example</h3><pre>Input:<br>s = &quot;cbaebabacd&quot;<br>p = &quot;abc&quot;</pre><pre>Output: [0, 6]</pre><p>Explanation:</p><p>The substrings starting at indices 0 and 6 are:</p><ul><li>cba</li><li>bac</li></ul><p>Both are anagrams of abc.</p><h3>Approach: Sliding Window + Frequency Array</h3><p>An anagram contains exactly the same characters with the same frequencies, but possibly in a different order.</p><p>For example:</p><p>abc and bca</p><p>Both contain:</p><pre>a = 1<br>b = 1<br>c = 1</pre><p>We can use a frequency array to store character counts.</p><p>Since the problem contains lowercase English letters, we only need an array of size 26.</p><p><strong>Step 1:</strong> Calculate the frequency of characters in p.</p><p><strong>Step 2:</strong> Create a window of size p.length() in string s.</p><p><strong>Step 3:</strong> Calculate the frequency of characters inside the current window.</p><p><strong>Step 4:</strong> Compare both frequency arrays.</p><p>If they are equal, store the starting index.</p><p><strong>Step 5:</strong> Slide the window by removing the outgoing character and adding the incoming character.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ho0fHo95jwvgBBfrfLbXNA.png" /></figure><h3>Dry Run</h3><pre>s = &quot;cbaebabacd&quot;<br>p = &quot;abc&quot;</pre><p>The window size is 3.</p><p>WindowAnagram?Starting IndexcbaYes0baeNo-aebNo-ebaNo-babNo-abaNo-bacYes6acdNo-</p><p>Answer:</p><p>[0, 6]</p><p><strong>Time Complexity:</strong> O(n + m + 26), effectively O(n + m).</p><p><strong>Space Complexity:</strong> O(26), effectively O(1).</p><h3>Important Learning</h3><p>Initially, I had also considered generating every substring and sorting it to check whether it was an anagram.</p><p>However, that approach performs unnecessary sorting operations.</p><p>Using a frequency array with a sliding window avoids repeatedly processing the entire substring.</p><p>This problem demonstrates how hashing-style frequency counting and sliding window can work together.</p><h3>7. LeetCode 146 — LRU Cache</h3><p><strong>Problem:</strong> Design a data structure that follows the Least Recently Used (LRU) cache policy.</p><p><a href="https://leetcode.com/problems/lru-cache/">Problem Link</a></p><p>This problem is slightly different from the previous three.</p><p>Here, we need to design a data structure rather than simply solve an array or string problem.</p><h3>What Is an LRU Cache?</h3><p>LRU stands for <strong>Least Recently Used</strong>.</p><p>It is a cache replacement policy in which the least recently accessed element is removed when the cache reaches its capacity and a new element needs to be inserted.</p><p>Consider a cache with capacity 2.</p><p>Operations:</p><pre>put(1, 10)<br>put(2, 20)<br>get(1)<br>put(3, 30)</pre><p>Let’s understand what happens.</p><ol><li>Insert key 1.</li><li>Insert key 2.</li><li>Access key 1, making it the most recently used.</li><li>Insert key 3.</li></ol><p>Now the cache is full.</p><p>Key 2 is removed because it is the least recently used.</p><p>The remaining cache contains keys 1 and 3.</p><h3>Which Data Structures Do We Need?</h3><p>To implement an LRU Cache efficiently, we combine two data structures:</p><p><strong>1. unordered_map</strong></p><p>Used to find a key quickly.</p><p>Average lookup time: O(1).</p><p><strong>2. Doubly Linked List</strong></p><p>Used to maintain the order of recently accessed elements.</p><p>A doubly linked list allows us to remove and move a node in O(1) time when we already have its address.</p><p>We maintain:</p><ul><li>Most recently used element at the front.</li><li>Least recently used element at the back.</li></ul><h3>Why Not Use Only unordered_map?</h3><p>An unordered_map provides fast searching, but it does not maintain the order in which elements were recently accessed.</p><p>Therefore, we need a doubly linked list to track usage order.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*81oigqNzlS1q5MQhTnEBwQ.png" /></figure><h3>Understanding the Operations</h3><p><strong>get(key)</strong></p><ul><li>Search for the key in unordered_map.</li><li>If it doesn’t exist, return -1.</li><li>Otherwise, move the node to the front.</li><li>Return its value.</li></ul><p><strong>put(key, value)</strong></p><ul><li>If the key already exists, update its value and move it to the front.</li><li>Otherwise, check whether the cache is full.</li><li>If full, remove the last node from the linked list and its key from the map.</li><li>Insert the new node at the front.</li></ul><h3>Time Complexity</h3><p>Operation Complexity</p><p>get() O(1)</p><p>averageput() O(1)</p><p>averageDeletenode O(1)</p><p>Insertnode O(1)</p><p><strong>Space Complexity:</strong> O(capacity).</p><h3>Important Learning</h3><p>This problem demonstrates something important about DSA:</p><p>Sometimes, a single data structure is not enough to achieve the required time complexity.</p><p>By combining an unordered_map and a doubly linked list, we can achieve average O(1) time for both get and put operations.</p><p>This is one of the most useful applications of hashing in system design and real-world caching mechanisms.</p><h3>8. What I Learned From These Four Problems</h3><p>After revising hashing and solving these problems, I understood that hashing is not limited to simply checking whether an element exists.</p><p>It can be combined with other patterns to solve more complex problems.</p><p>Here are the major concepts I learned:</p><p>ProblemMain ConceptData StructureLC 128Consecutive Sequenceunordered_setLC 560Prefix Sum + Frequencyunordered_mapLC 438Sliding Window + Frequency CountingFrequency ArrayLC 146LRU Cacheunordered_map + Doubly Linked List</p><p>Some important takeaways:</p><ol><li><strong>Hashing:</strong> Provides efficient average-case searching, insertion, and deletion.</li><li><strong>unordered_map:</strong> Useful for storing frequencies and key-value relationships.</li><li><strong>unordered_set:</strong> Useful for maintaining unique elements and checking existence.</li><li><strong>Prefix Sum + Hash Map:</strong> Helps solve subarray sum problems, especially when negative numbers are present.</li><li><strong>Sliding Window + Frequency Counting:</strong> Helps solve anagram and character-frequency problems.</li><li><strong>Hash Map + Doubly Linked List:</strong> Helps build efficient data structures such as LRU Cache.</li></ol><h3>9. Final Thoughts</h3><p>Hashing is one of the most important concepts in DSA because it helps reduce unnecessary searching and makes many solutions more efficient.</p><p>Before this revision, I had already used maps and sets in multiple problems, but I wanted to understand their differences, internal working, and practical applications more clearly.</p><p>These four problems helped me connect hashing with different DSA patterns, including prefix sums, sliding windows, and linked lists.</p><p>My biggest takeaway is that learning a data structure is not just about knowing its functions. It is about understanding when and how to combine it with other data structures to solve problems efficiently.</p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#Hashing #Leetcode #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=defd0b66f599" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Minimum Number of Refueling Stops — Understanding Greedy + Max Heap]]></title>
            <link>https://medium.com/@harshjaiswal2707/minimum-number-of-refueling-stops-understanding-greedy-max-heap-b895168366a9?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/b895168366a9</guid>
            <category><![CDATA[leetcode]]></category>
            <category><![CDATA[sad]]></category>
            <category><![CDATA[interviewproblem]]></category>
            <category><![CDATA[refueling-stop]]></category>
            <category><![CDATA[heapwithgreedy]]></category>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Mon, 26 Oct 2026 14:47:46 GMT</pubDate>
            <atom:updated>2026-10-26T14:47:46.581Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction</h3><p>Today, I solved <strong>LeetCode 871 — Minimum Number of Refueling Stops</strong>, and it helped me understand how <strong>Greedy Algorithms and Heaps work together</strong> to solve an optimization problem.</p><p>At first, the problem looks like a simple journey-planning question. However, the real challenge is deciding <strong>when to refuel and which station’s fuel to choose</strong> so that we reach our destination with the minimum number of stops.</p><p>Let’s break down the intuition, algorithm, and implementation.</p><h3>1. Understanding the Problem</h3><p>Imagine that you are driving a car toward a destination.</p><p>You are given:</p><ul><li>target: The distance to your destination.</li><li>startFuel: The amount of fuel available initially.</li><li>stations: An array where each station contains its position and the amount of fuel it provides.</li></ul><p>Your task is to reach the destination using the <strong>minimum possible number of refueling stops</strong>.</p><p>For example:</p><pre>target = 100;<br>startFuel = 10;</pre><pre>stations = {<br>    {10, 60},<br>    {20, 30},<br>    {30, 30},<br>    {60, 40}<br>};</pre><p>Each pair represents {position, fuel}.</p><p>For example, {10, 60} means a petrol station is located 10 units from the starting point and provides 60 units of fuel.</p><p>The answer is 2, because we can reach the destination with only two refueling stops.</p><h3>2. Real-Life Analogy: Planning a Long Road Trip</h3><p>Imagine you are driving a car with limited fuel.</p><p>You encounter several petrol stations along the highway. Some provide 20 litres, some provide 40 litres, and others provide 60 litres.</p><p>Would you stop at every petrol station?</p><p>Not necessarily!</p><p>Instead, you can keep moving while your current fuel is sufficient. If you later discover that you cannot reach the next destination, you can use the fuel available from a station you have already passed.</p><p>The smart strategy is to choose the station that provides the <strong>maximum available fuel</strong>.</p><p>This is the intuition behind combining Greedy and Max Heap.</p><h3>3. Why Do We Need a Greedy Algorithm?</h3><p>A Greedy Algorithm makes a locally optimal choice at each step in the hope of achieving a globally optimal solution.</p><p>In this problem, we want to minimize the number of refueling stops.</p><p>Whenever our fuel becomes insufficient, we choose the largest available fuel amount from the stations we have already reached.</p><p>Why?</p><p>Because a larger fuel amount can help us travel farther with the same number of stops.</p><p>We do not refuel unnecessarily at every station. Instead, we postpone the decision until additional fuel is needed.</p><p><strong>Important:</strong> We only consider stations that we can reach. We cannot use fuel from a station that is still ahead of us.</p><h3>4. Why Do We Need a Max Heap?</h3><p>A Max Heap allows us to retrieve the largest available fuel amount efficiently.</p><p>In C++, the default priority_queue&lt;int&gt; is a Max Heap.</p><pre>priority_queue&lt;int&gt; pq;</pre><pre>pq.push(60);<br>pq.push(30);<br>pq.push(40);</pre><p>Now:</p><pre>pq.top();  // 60</pre><p>The largest value is always available at the top.</p><p>The key operations are:</p><ul><li>pq.push(fuel): Add fuel from a reachable station.</li><li>pq.top(): Access the largest available fuel amount.</li><li>pq.pop(): Remove that amount after using it.</li></ul><p>Both insertion and removal take (O(\log n)) time, while accessing the top takes (O(1)) time.</p><p>The heap helps us avoid repeatedly searching through all previously reached stations to find the one with the most fuel.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0tCl0A5cvYeMMNY8T3rFBw.png" /></figure><h3>Understanding the Important Variables</h3><p><strong>fuel — Current available fuel</strong></p><p>It tracks how much fuel remains after travelling to the current position.</p><p><strong>prev — Previous position</strong></p><p>It helps calculate the distance travelled between consecutive stations.</p><p><strong>pq — Max Heap</strong></p><p>It stores the fuel amounts of stations we have already reached but have not yet selected for refueling.</p><p><strong>ans — Number of refueling stops</strong></p><p>It increases only when we actually take fuel from the heap.</p><h3>The Most Important Logic</h3><h3>Step 1: Calculate the distance</h3><pre>fuel -= (pos - prev);</pre><p>Suppose the previous position is 20 and the next position is 60.</p><p>The distance travelled is:</p><p>[<br>60–20 = 40<br>]</p><p>If we had 50 units of fuel, we would have 10 remaining.</p><h3>Step 2: Check whether the fuel is negative</h3><pre>while (fuel &lt; 0)</pre><p>Negative fuel means that our current fuel is insufficient to reach the current position.</p><p>We must refuel before proceeding.</p><h3>Step 3: Choose the maximum available fuel</h3><pre>if (pq.empty()) {<br>    return -1;<br>}</pre><pre>fuel += pq.top();<br>pq.pop();<br>ans++;</pre><p>If the heap is empty, no previously reached station can provide additional fuel, so the destination is unreachable.</p><p>Otherwise, we take the maximum available fuel, remove it from the heap, and increment our stop count.</p><p>The while loop is important because we may need more than one previously available station&#39;s fuel to reach the current position.</p><h3>Step 4: Add the current station</h3><pre>if (i &lt; stations.size()) {<br>    pq.push(stations[i][1]);<br>}</pre><p>Once we reach a station, its fuel becomes available for future use.</p><p>Notice that <strong>adding fuel to the heap does not mean that we have refueled</strong>. It only means that the station is now an available option.</p><p>We count a stop only when we take its fuel from the heap.</p><h3>Dry Run</h3><p>Consider the same example:</p><pre>target = 100;<br>startFuel = 10;</pre><pre>stations = {<br>    {10, 60},<br>    {20, 30},<br>    {30, 30},<br>    {60, 40}<br>};</pre><p>At the destination, the remaining fuel is:</p><p>[<br>10–40 + 40 = 10<br>]</p><p>We successfully reach the destination with <strong>2 refueling stops</strong>.</p><h3>9. Time and Space Complexity</h3><p>Let (n) be the number of fuel stations.</p><p><strong>Time Complexity: (O(n\log n))</strong></p><p>Each station’s fuel is inserted into the heap at most once and removed at most once. Each heap insertion or removal takes (O(\log n)) time.</p><p><strong>Space Complexity: (O(n))</strong></p><p>In the worst case, the heap can contain fuel amounts from all (n) stations.</p><h3>10. What I Learned Today</h3><p>Solving this problem strengthened my understanding of three important ideas:</p><ol><li><strong>Greedy:</strong> Make a strategic choice when additional fuel is required.</li><li><strong>Max Heap:</strong> Efficiently select the largest available fuel amount.</li><li><strong>Delayed decision-making:</strong> Keep available options in the heap and commit to a refueling stop only when necessary.</li></ol><p>The most important lesson is that a heap is not just a data structure for finding maximum or minimum values. When combined with a greedy strategy, it can help solve optimization problems efficiently.</p><h3>Conclusion</h3><p>LeetCode 871 was a great exercise in understanding how two concepts can work together to produce an efficient solution.</p><p>My key takeaway:</p><p><strong>Greedy decides when a choice is necessary, and the Max Heap helps us choose the best available option.</strong></p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#HeapWithGreedy #RefuelingStops #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b895168366a9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Find Median and Merge List using Heap]]></title>
            <link>https://medium.com/@harshjaiswal2707/find-median-and-merge-list-using-heap-7fb477e429af?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/7fb477e429af</guid>
            <category><![CDATA[median]]></category>
            <category><![CDATA[interviewproblem]]></category>
            <category><![CDATA[sortedlist]]></category>
            <category><![CDATA[leetcode]]></category>
            <category><![CDATA[sad]]></category>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Sun, 25 Oct 2026 14:25:44 GMT</pubDate>
            <atom:updated>2026-10-25T14:25:44.876Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introduction: When a Heap Becomes More Than a Data Structure</h3><p>While solving LeetCode problems, I realized something interesting: a heap is not just a data structure for finding the largest or smallest element. It becomes much more powerful when we use it to solve a specific problem efficiently.</p><p>Recently, I solved two important heap problems:</p><ul><li><strong>LeetCode 295 — Find Median from Data Stream</strong></li><li><strong>LeetCode 23 — Merge k Sorted Lists</strong></li></ul><p>At first, these problems look different. One asks us to find a median as numbers arrive, while the other asks us to merge multiple sorted linked lists.</p><p>However, both problems share the same fundamental idea:</p><p><strong>Keep the most useful elements accessible so that we do not have to search through all the data repeatedly.</strong></p><p>Let’s understand both problems through real-life analogies, C++ code, and the reasoning behind the heap operations.</p><h3>1. Heap Fundamentals: Max Heap vs. Min Heap</h3><p>Before solving the problems, let’s quickly revise two types of heaps.</p><h3>Max Heap</h3><p>In a Max Heap, the largest element is always available at the top.</p><pre>priority_queue&lt;int&gt; maxHeap;</pre><p>For example, if we insert 10, 4, 20, 7, then:</p><pre>Top = 20</pre><p>We can access the largest element in <strong>O(1)</strong> time.</p><h3>Min Heap</h3><p>In a Min Heap, the smallest element is always available at the top.</p><pre>priority_queue&lt;int, vector&lt;int&gt;, greater&lt;int&gt;&gt; minHeap;</pre><p>For the same numbers:</p><pre>Top = 4</pre><p>We can access the smallest element in <strong>O(1)</strong> time.</p><p>The important difference is not how we insert numbers, but which element we can access immediately.</p><p>This distinction becomes especially useful in the following two problems.</p><h3>2. LeetCode 295 — Find Median from Data Stream</h3><p><strong>Problem:</strong> Design a data structure that supports adding numbers one at a time and finding the median of all numbers added so far.</p><h3>Real-Life Analogy: Dividing a Classroom into Two Groups</h3><p>Imagine a classroom where students arrive one by one, and we need to find the middle height of all students after every arrival.</p><p>We could sort all students every time someone new arrives. However, repeatedly sorting the entire collection is inefficient.</p><p>Instead, let’s divide the students into two groups:</p><ul><li><strong>Left group:</strong> The shorter half of the students.</li><li><strong>Right group:</strong> The taller half of the students.</li></ul><p>We only need two students to find the middle point:</p><ul><li>The tallest student in the left group.</li><li>The shortest student in the right group.</li></ul><p>This is exactly where using a Max Heap and a Min Heap together becomes powerful.</p><h3>Why Do We Need Both Max Heap and Min Heap?</h3><p>We use:</p><pre>priority_queue&lt;int&gt; left;<br>priority_queue&lt;int, vector&lt;int&gt;, greater&lt;int&gt;&gt; right;</pre><p>The left heap is a Max Heap that stores the smaller half of the numbers.</p><p>The right heap is a Min Heap that stores the larger half of the numbers.</p><p>Conceptually:</p><pre>LEFT HALF                 RIGHT HALF</pre><pre>         Max Heap                    Min Heap</pre><pre>       Largest of left             Smallest of right<br>              |                           |<br>            left.top()                 right.top()</pre><p>The two heaps maintain two important rules:</p><ol><li>Every element in left must be less than or equal to every element in right.</li><li>The size of left must equal the size of right, or exceed it by exactly one.</li></ol><p>These rules ensure that the middle element or middle two elements are always accessible.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*U3BRk7Xcg4nu01y0Oo7h-A.png" /></figure><h3>Understanding addNum()</h3><h3>Step 1: Insert into the left heap</h3><pre>left.push(num);</pre><p>We initially insert the new number into the Max Heap.</p><h3>Step 2: Move the largest left element into the right heap</h3><pre>right.push(left.top());<br>left.pop();</pre><p>Because left is a Max Heap, its top is the largest element in the left group.</p><p>Moving this element to the Min Heap helps maintain the ordering between the two groups.</p><h3>Step 3: Balance the heaps</h3><pre>if (right.size() &gt; left.size()) {<br>    left.push(right.top());<br>    right.pop();<br>}</pre><p>If the right heap becomes larger than the left heap, we move the smallest element from the right heap back to the left.</p><p>After balancing, the left heap contains either the same number of elements as the right heap or exactly one more.</p><h3>Understanding findMedian()</h3><p>There are two possible situations.</p><p><strong>Case 1: The left heap has one extra element.</strong></p><p>The total number of elements is odd, so the median is:</p><pre>return left.top();</pre><p><strong>Case 2: Both heaps have equal sizes.</strong></p><p>The total number of elements is even, so the median is the average of the two middle elements:</p><pre>return ((double)left.top() + right.top()) / 2.0;</pre><p>For example, if the numbers are 1, 3, 5, 7, the left heap&#39;s top is 3 and the right heap&#39;s top is 5.</p><p>Therefore:</p><pre>Median = (3 + 5) / 2 = 4</pre><h3>Complexity Analysis</h3><p>OperationTime ComplexityaddNum()O(log n)findMedian()O(1)SpaceO(n)</p><p><strong>Key takeaway:</strong> Using a Max Heap and a Min Heap together allows us to maintain the middle of a continuously changing dataset without sorting all the numbers every time.</p><h3>3. LeetCode 23 — Merge k Sorted Lists</h3><p><strong>Problem:</strong> Given an array of k sorted linked lists, merge them into one sorted linked list.</p><h3>Real-Life Analogy: Merging Multiple Sorted Queues</h3><p>Imagine three queues at a ticket counter. Each queue is already arranged in increasing order of ticket numbers.</p><pre>Queue 1: 1 → 4 → 7<br>Queue 2: 2 → 5 → 8<br>Queue 3: 3 → 6 → 9</pre><p>We need to combine these queues into one sorted sequence.</p><p>We could examine every remaining element across all queues repeatedly to find the smallest one. However, that would require unnecessary comparisons.</p><p>Instead, let’s compare only the first available element from each queue.</p><p>Initially, these elements are:</p><pre>Queue 1 → 1<br>Queue 2 → 2<br>Queue 3 → 3</pre><p>The smallest element is 1. We take it, add it to the merged list, and then consider the next element from Queue 1, which is 4.</p><p>Now our available candidates are 2, 3, 4.</p><p>We repeat this process until all lists are merged.</p><p>This is exactly how a Min Heap helps us solve LC 23 efficiently.</p><h3>Why Do We Need a Min Heap?</h3><p>We only need the smallest available node across the lists at each step.</p><p>Therefore, we use:</p><pre>priority_queue&lt;ListNode*, vector&lt;ListNode*&gt;, Compare&gt; pq;</pre><p>Here:</p><ul><li>ListNode* stores pointers to linked-list nodes.</li><li>vector&lt;ListNode*&gt; provides the heap&#39;s underlying storage.</li><li>Compare makes the smallest node value appear at the top.</li></ul><p>Unlike LC 295, we don’t need both a Max Heap and a Min Heap here. A single Min Heap is sufficient because our goal is always to find the smallest available node.</p><h3>Custom Comparator Explained</h3><pre>struct Compare {<br>    bool operator()(ListNode* a, ListNode* b) {<br>        return a-&gt;val &gt; b-&gt;val;<br>    }<br>};</pre><p>Suppose:</p><pre>a-&gt;val = 8<br>b-&gt;val = 3</pre><p>The comparison:</p><pre>return a-&gt;val &gt; b-&gt;val;</pre><p>evaluates to true.</p><p>This gives the smaller value higher priority in our Min Heap, so the node containing 3 comes before the node containing 8.</p><p>The comparator determines the heap’s ordering based on node values, rather than pointer addresses.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gn_Y3RLxXQwRhpK_cLDhSw.png" /></figure><h3>Understanding the Algorithm</h3><h3>Step 1: Insert the first node of every list</h3><pre>for (auto list : lists) {<br>    if (list != nullptr) {<br>        pq.push(list);<br>    }<br>}</pre><p>Notice something important: <strong>we do not insert every node into the heap at the beginning.</strong></p><p>We insert only the first node of each non-empty list because it is the smallest available element in that list.</p><h3>Step 2: Extract the smallest node</h3><pre>ListNode* node = pq.top();<br>pq.pop();</pre><p>The Min Heap gives us the smallest available node.</p><h3>Step 3: Connect it to the merged list</h3><pre>tail-&gt;next = node;<br>tail = tail-&gt;next;</pre><p>We connect the existing node to our result.</p><p>We are reusing the linked-list nodes, not creating a new node for every value.</p><p>The dummy node provides a convenient starting point, and tail tracks the last node in the merged list.</p><h3>Step 4: Insert the next node from the same list</h3><pre>if (node-&gt;next != nullptr) {<br>    pq.push(node-&gt;next);<br>}</pre><p>After selecting a node, we insert its next node into the heap if one exists.</p><p>We repeat the process until the heap becomes empty.</p><h3>Complexity Analysis</h3><p>Let N be the total number of nodes across all lists, and k be the number of lists.</p><p>OperationComplexityInitial heap constructionO(k log k)Processing all nodesO(N log k)Overall timeO(N log k)Auxiliary heap spaceO(k)</p><p>The merged list reuses the original nodes, so the algorithm does not need an additional O(N) collection of output nodes.</p><p><strong>Key takeaway:</strong> The Min Heap lets us select the smallest available node efficiently, while linked-list pointers allow us to build the final sorted list without recreating every node.</p><h3>Final Takeaways</h3><p>Solving these two problems helped me understand that mastering heaps is not just about knowing priority_queue syntax.</p><p>It is about identifying the element that matters at each step and keeping it accessible.</p><ul><li><strong>LC 295:</strong> A Max Heap and a Min Heap work together to maintain the median efficiently.</li><li><strong>LC 23:</strong> A Min Heap works with linked-list pointers to merge multiple sorted lists efficiently.</li><li><strong>Custom comparators:</strong> They let us control how complex elements, such as linked-list nodes, are prioritized.</li><li><strong>The bigger pattern:</strong> Use a heap when repeatedly finding the next smallest, largest, or otherwise highest-priority element would be expensive with a simple unsorted collection.</li></ul><p>These problems demonstrate two valuable applications of heaps: maintaining a moving boundary in a data stream and efficiently merging multiple sorted sequences.</p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#Median #SortedList #Heap #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7fb477e429af" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mastering Heap Patterns “Top K” : Maintain Heap Of Size K]]></title>
            <link>https://medium.com/@harshjaiswal2707/mastering-heap-patterns-top-k-maintain-heap-of-size-k-fd0685d87de7?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/fd0685d87de7</guid>
            <category><![CDATA[sad]]></category>
            <category><![CDATA[heap]]></category>
            <category><![CDATA[top-k]]></category>
            <category><![CDATA[enjoyproblem]]></category>
            <category><![CDATA[leetcode]]></category>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Fri, 23 Oct 2026 06:34:58 GMT</pubDate>
            <atom:updated>2026-10-23T06:34:58.289Z</atom:updated>
            <content:encoded><![CDATA[<p>Here , We understand about Custom Comparator. While practicing Heap problems on LeetCode, I solved three important problems that helped me understand how heaps can solve different problems efficiently:</p><ul><li><strong>LC 692 — Top K Frequent Words</strong></li><li><strong>LC 347 — Top K Frequent Elements</strong></li><li><strong>LC 973 — K Closest Points to Origin</strong></li></ul><p>All three use heaps, but each problem teaches a different way to prioritize elements.</p><p>Let’s understand them with real-life analogies, C++ code, and the patterns behind them.</p><h3>1. LC 692 — Top K Frequent Words</h3><p><strong>Pattern: Hashing + Max Heap + Custom Comparato</strong></p><h3>Understanding the problem</h3><p>Given an array of words and an integer k, return the k most frequent words.</p><p>If two words have the same frequency, the lexicographically smaller word must come first.</p><p>Example:</p><pre>words = {&quot;i&quot;, &quot;love&quot;, &quot;leetcode&quot;, &quot;i&quot;, &quot;love&quot;, &quot;coding&quot;};<br>k = 2;</pre><p>Output:</p><pre>{&quot;i&quot;, &quot;love&quot;}</pre><p>Both i and love appear twice. Since i comes before love alphabetically, it ranks first.</p><h3>Real-life analogy: A popularity ranking system</h3><p>Imagine a teacher wants to identify the two most popular students based on votes.</p><ul><li>More votes mean a higher rank.</li><li>If two students receive the same votes, their names are ranked alphabetically.</li></ul><p>We need a system that follows both rules automatically. That’s exactly what a heap with a custom comparator can do.</p><h3>Step 1: Count word frequencies</h3><pre>map&lt;string, int&gt; mp;</pre><pre>for (auto word : words) {<br>    mp[word]++;<br>}</pre><p>The map stores each unique word and its frequency.</p><p>WordFrequencycoding1i2leetcode1love2</p><h3>Step 2: Understand the custom comparator</h3><p>We store each entry as:</p><pre>{frequency, word}</pre><p>The comparator must enforce two rules:</p><ol><li>Higher frequency gets higher priority.</li><li>If frequencies are equal, the alphabetically smaller word gets higher priority.</li></ol><pre>struct Compare {<br>    bool operator()(const pair&lt;int, string&gt;&amp; a,<br>                    const pair&lt;int, string&gt;&amp; b) {<br>        if (a.first == b.first) {<br>            return a.second &gt; b.second;<br>        }</pre><pre>        return a.first &lt; b.first;<br>    }<br>};</pre><p><strong>How does it work?</strong></p><p><strong>Case A: Different frequencies</strong></p><pre>a = {2, &quot;apple&quot;};<br>b = {5, &quot;banana&quot;};</pre><pre>return a.first &lt; b.first;  // true</pre><p>The lower-frequency entry has lower priority, so the word with frequency 5 ranks higher.</p><p><strong>Case B: Equal frequencies</strong></p><pre>a = {3, &quot;mango&quot;};<br>b = {3, &quot;apple&quot;};</pre><pre>return a.second &gt; b.second;  // true</pre><p>mango comes after apple alphabetically, so mango gets lower priority.</p><p><strong><em>Remember: in a C++ </em></strong><strong><em>priority_queue, returning </em></strong><strong><em>true means the first entry has lower priority than the second. This is why the comparison rules may initially feel opposite to what we expect.</em></strong></p><h3>Step 3: Create the heap</h3><pre>priority_queue&lt;<br>    pair&lt;int, string&gt;,<br>    vector&lt;pair&lt;int, string&gt;&gt;,<br>    Compare<br>&gt; pq;</pre><p>The three template arguments mean:</p><ul><li>pair&lt;int, string&gt;: what we store.</li><li>vector&lt;pair&lt;int, string&gt;&gt;: the underlying container.</li><li>Compare: the rule that decides priority.</li></ul><p>Now insert every frequency-word pair:</p><pre>for (auto it : mp) {<br>    pq.push({it.second, it.first});<br>}</pre><p>Extract the top k words:</p><pre>vector&lt;string&gt; ans;</pre><pre>for (int i = 0; i &lt; k; i++) {<br>    ans.push_back(pq.top().second);<br>    pq.pop();<br>}</pre><pre>return ans;</pre><p>The heap returns i first and love second.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZWyOXV12BzLB7nXowtxxIg.png" /></figure><p><strong>Complexity:</strong> With n total words and m unique words, the time complexity is O(n log m + m log m + k log m), and auxiliary space is O(m).</p><h3>Key takeaway</h3><p>LC 692 taught me that a heap is not just about finding the largest or smallest element. A custom comparator lets us define exactly what “highest priority” means.</p><h3>2. LC 347 — Top K Frequent Elements</h3><p><strong>Pattern: Hashing + Max Heap</strong></p><h3>Understanding the problem</h3><p>Given an integer array, return the k most frequent elements.</p><p>Example:</p><pre>nums = {1, 1, 1, 2, 2, 3};<br>k = 2;</pre><p>Output:</p><pre>{1, 2}</pre><h3>Real-life analogy: A product sales leaderboard</h3><p>Imagine an online store wants to find its two best-selling products.</p><ul><li>Product 1 sold 3 units.</li><li>Product 2 sold 2 units.</li><li>Product 3 sold 1 unit.</li></ul><p>Instead of manually sorting every product, we can count sales and use a max heap to retrieve the highest-frequency products first.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VXjNfqQhiESGJrgSZIlIRg.png" /></figure><h3>How it works</h3><p><strong>Step 1:</strong> Count each number’s frequency.</p><p><strong>Step 2:</strong> Insert {frequency, element} into the max heap.</p><p><strong>Step 3:</strong> Pop the heap k times and collect each element.</p><p>Because the default priority_queue&lt;pair&lt;int,int&gt;&gt; compares the first value first, the highest frequency appears at the top. If frequencies tie, the larger integer appears first, which is acceptable because LC 347 does not require a particular output order.</p><p><strong>Complexity:</strong> With m unique numbers, the time complexity is O(n log m + m log m + k log m), with O(m) auxiliary space.</p><h3>Key takeaway</h3><p>LC 347 helped me understand the classic <strong>frequency counting + max heap</strong> pattern.</p><h3>3. LC 973 — K Closest Points to Origin</h3><p><strong>Pattern: Distance Calculation + Min Heap</strong></p><h3>Understanding the problem</h3><p>Given points on a 2D plane, return the k points closest to the origin (0,0).</p><p>Example:</p><pre>points = {{1,3}, {-2,2}, {5,8}, {0,1}};<br>k = 2;</pre><p>Output can be:</p><pre>{{0,1}, {-2,2}}</pre><h3>Real-life analogy: Finding the nearest delivery locations</h3><p>Imagine a delivery service operating from a warehouse at (0,0).</p><p>There are many delivery locations, and the service needs to identify the two closest ones.</p><p>We calculate each location’s distance from the warehouse and use a min heap to retrieve the closest locations first.</p><h3>Step 1: Calculate squared distance</h3><p>The Euclidean distance is:</p><p>[<br>d=\sqrt{x²+y²}<br>]</p><p>For comparison, we can use squared distance instead:</p><p>[<br>d²=x²+y²<br>]</p><p>The square root is unnecessary because squaring preserves the order of nonnegative distances.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Wep94H2dyY5WnztA780uAQ.png" /></figure><h3>How it works</h3><p><strong>Step 1:</strong> Calculate the squared distance of every point.</p><p><strong>Step 2:</strong> Store {distance, index} in a min heap.</p><p><strong>Step 3:</strong> Extract the smallest k distances and use their indices to retrieve the original points.</p><p>The greater&lt;pair&lt;int,int&gt;&gt; comparator creates a min heap. The smallest distance appears at the top.</p><p><strong>Complexity:</strong> For n points, inserting all points and extracting k takes O(n log n + k log n) time and O(n) auxiliary space.</p><h3>Key takeaway</h3><p>LC 973 taught me that heaps can prioritize elements based on a calculated property, not just their original values.</p><h3>4. What these three problems taught me</h3><p>ProblemCore patternPriority rule</p><p>LC 692Frequency + Custom HeapHighest frequency; alphabetical tie-break</p><p>LC 347Frequency + Max HeapHighest frequency</p><p>LC 973Distance + Min HeapSmallest distance</p><h3>5. Final revision notes</h3><ul><li><strong>Max heap:</strong> By default, the largest value comes first.</li><li><strong>Min heap:</strong> Use greater&lt;T&gt; to prioritize the smallest value.</li><li><strong>Frequency problems:</strong> Count occurrences first, then prioritize by frequency.</li><li><strong>Custom comparator:</strong> Useful when the problem has multiple ranking rules.</li><li><strong>Distance problems:</strong> Calculate the relevant distance or score, then use a heap to retrieve the best candidates.</li><li><strong>Optimization:</strong> When k is much smaller than the number of unique elements, a heap limited to size k can reduce the heap-related work to O(m log k) after frequency counting.</li></ul><p>Solving these three problems helped me understand that the most important part of a heap problem is not just choosing a min heap or max heap — it’s deciding what priority each element should have.</p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#TopK #Heap #Leetcode #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fd0685d87de7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Basics of Heap: Stop Searching, Start Understanding!]]></title>
            <link>https://medium.com/@harshjaiswal2707/basics-of-heap-stop-searching-start-understanding-694fbee9c27a?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/694fbee9c27a</guid>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Thu, 22 Oct 2026 06:14:22 GMT</pubDate>
            <atom:updated>2026-10-22T06:14:22.605Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>A beginner-friendly guide to Min Heap, Max Heap, and the fundamentals of Heap in C++.</strong></p><p>Today, I started revising the Heap data structure in DSA. I revised the fundamentals and solved three important LeetCode problems:</p><p><strong>Last Stone Weight (1046),</strong></p><p><strong>Kth Largest Element in a Stream (703),</strong></p><p><strong>Kth Largest Element in an Array (215).</strong></p><p><strong>One of the best moments was solving LeetCode 215 in under 5 minutes</strong> <strong>during revision</strong>. It reminded me how understanding a pattern makes problem-solving much faster.</p><p>In this article, I’ll explain Heap fundamentals, followed by the approach and key learnings from each problem.</p><h3>1. What Is a Heap?</h3><p>A <strong>Heap is a Complete Binary Tree that follows a specific ordering property</strong>, called the Heap Property.</p><p>A Complete Binary Tree is a tree in which every level is completely filled except possibly the last level, which is filled from left to right.</p><p>There are two main types of heaps.</p><h3>Min Heap</h3><p>In a Min Heap, the parent node is always smaller than or equal to its children.</p><p><strong>Example:</strong></p><pre>        10<br>       /  \<br>      20   15<br>     / \<br>    30  40</pre><p>The smallest element, 10, is at the root.</p><h3>Max Heap</h3><p>In a Max Heap, the parent node is always greater than or equal to its children.</p><p><strong>Example:</strong></p><pre>        50<br>       /  \<br>      30   40<br>     / \<br>    10  20</pre><p>The largest element, 50, is at the root.</p><p><strong>Important:</strong> A Heap is not a completely sorted tree. Only the parent-child ordering property is guaranteed.</p><h3>2. Heap in C++ Using priority_queue</h3><p>C++ provides a built-in priority_queue that makes heap operations convenient.</p><h3>Max Heap</h3><pre>priority_queue&lt;int&gt; pq;</pre><pre>pq.push(10);<br>pq.push(30);<br>pq.push(20);</pre><pre>cout &lt;&lt; pq.top();  // 30</pre><p>By default, priority_queue implements a Max Heap, so the largest element is available at the top.</p><h3>Min Heap</h3><pre>priority_queue&lt;int, vector&lt;int&gt;, greater&lt;int&gt;&gt; pq;</pre><pre>pq.push(10);<br>pq.push(30);<br>pq.push(20);</pre><pre>cout &lt;&lt; pq.top();  // 10</pre><p>Using greater&lt;int&gt; creates a Min Heap, so the smallest element is available at the top.</p><h3>Time Complexity</h3><p>OperationComplexityInsert (push)O(log n)Access top (top)O(1)Remove top (pop)O(log n)</p><p>Heaps are especially useful when we repeatedly need the smallest or largest element without sorting the entire collection.</p><h3>3. LeetCode 1046 — Last Stone Weight</h3><h3>Approach: Max Heap</h3><p>We are given stones with different weights. In each operation, we select the two heaviest stones and smash them together.</p><ul><li>If both stones have the same weight, both are destroyed.</li><li>If their weights differ, the remaining stone has the difference in weight.</li><li>We repeat until at most one stone remains.</li></ul><p>Since we always need the two heaviest stones, a Max Heap is the perfect choice.</p><h3>Real-Life Analogy: A Boxing Match Between the Two Strongest Players</h3><p>Imagine a competition in which players have different strength levels.</p><p>In every round, the two strongest players compete:</p><ul><li>If both have equal strength, both are eliminated.</li><li>If one is stronger, the weaker player is eliminated, and the stronger player loses strength equal to the weaker player’s strength.</li><li>The remaining player returns to the competition.</li></ul><p>We repeat this process until at most one player remains.</p><p>This is exactly the idea behind Last Stone Weight.</p><p>Since we always need the two heaviest stones, a <strong>Max Heap</strong> is the right choice.</p><h3>Example</h3><pre>stones = [2, 7, 4, 1, 8, 1]</pre><p>Initially, the heaviest stones are 8 and 7.</p><p><strong>Round 1:</strong></p><pre>8 vs 7 → Remaining weight = 1</pre><p>The remaining stone weighs 1.</p><p>Now the stones effectively become:</p><pre>[2, 4, 1, 1, 1]</pre><p><strong>Round 2:</strong></p><pre>4 vs 2 → Remaining weight = 2</pre><p>Remaining stones:</p><pre>[2, 1, 1, 1]</pre><p><strong>Round 3:</strong></p><pre>2 vs 1 → Remaining weight = 1</pre><p>Remaining stones:</p><pre>[1, 1, 1]</pre><p><strong>Round 4:</strong></p><pre>1 vs 1 → Both destroyed</pre><p>One stone of weight 1 remains.</p><p><strong>Final Answer: 1</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J5WLvrOzZDdB5ltIVWFNIQ.png" /></figure><h3>Dry Run</h3><p>Suppose:</p><pre>stones = [2, 7, 4, 1, 8, 1]</pre><p>The two heaviest stones are selected in each round.</p><p>OperationResult8 and 714 and 222 and 111 and 1Both destroyed</p><p>The remaining stone has weight 1.</p><h3>Complexity</h3><ul><li><strong>Time:</strong> O(n log n)</li><li><strong>Space:</strong> O(n)</li></ul><p><strong>Key learning:</strong> When a problem repeatedly asks for the maximum element, think of a Max Heap.</p><h3>4. LeetCode 703 — Kth Largest Element in a Stream</h3><h3>Approach: Min Heap of Size K</h3><p>We need to find the Kth largest element as new numbers arrive in a stream.</p><p>Instead of storing every element in a sorted collection, we maintain a Min Heap containing only the K largest elements seen so far.</p><p>The smallest element among those K elements will be the Kth largest overall.</p><h4>Real-Life Analogy: Maintaining the Top 3 Students in a School</h4><p>Imagine a school where students keep receiving new exam scores.</p><p>You don’t need the complete ranking of every student. You only need to know the third-highest score at any moment.</p><p>Suppose the scores arriving are:</p><pre>4, 5, 8, 2</pre><p>And we need the third-largest score, so k = 3.</p><p>The three highest scores are:</p><pre>4, 5, 8</pre><p>The third-largest score is 4.</p><p>Now a new student arrives with a score of 10.</p><p>The three highest scores become:</p><pre>5, 8, 10</pre><p>The third-largest score is now 5.</p><p>Notice that we don’t need to maintain every score in sorted order. We only need the top three.</p><h3>Why Use a Min Heap?</h3><p>We maintain a Min Heap of size k.</p><p>Think of it as a leaderboard that can hold only three students.</p><p>Whenever a new score arrives:</p><ol><li>Add the score to the heap.</li><li>If more than three scores are present, remove the smallest.</li><li>The top of the heap is the third-largest score.</li></ol><p>Why Min Heap? Because the smallest score among the top three is exactly the third-largest overall.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Vr5-POGM0dn2NYlMcitriw.png" /></figure><h3>Dry Run</h3><p>Suppose k = 3 and the stream is:</p><pre>[4, 5, 8, 2]</pre><p>After processing these numbers, the three largest elements are 4, 5, 8.</p><p>The Min Heap’s top is 4, which is the third-largest element.</p><p>Now, a new value 10 arrives.</p><pre>Before: [4, 5, 8]<br>Add: 10<br>Remove smallest: 4</pre><pre>After: [5, 8, 10]</pre><p>The third-largest element is now 5.</p><h3>Complexity</h3><ul><li><strong>Initialization:</strong> O(n log k) with repeated insertion.</li><li><strong>Each </strong><strong>add() operation:</strong> O(log k)</li><li><strong>Auxiliary space:</strong> O(k)</li></ul><p><strong>Key learning:</strong> When the input keeps changing and you need the Kth largest element, maintain a Min Heap of size K.</p><h3>5. LeetCode 215 — Kth Largest Element in an Array</h3><h3>Approach: Min Heap of Size K</h3><p>This problem looks similar to LeetCode 703, but there is an important difference: here, the entire array is available at once.</p><p>We need to return the Kth largest element without necessarily sorting the entire array.</p><p>The same pattern works:</p><ol><li>Insert each number into a Min Heap.</li><li>If the heap size exceeds K, remove the smallest element.</li><li>After processing every number, the heap contains the K largest elements.</li><li>The top element is the Kth largest.</li></ol><h4>Real-Life Analogy: Finding the Second-Highest Bid in an Auction</h4><p>Imagine an auction with bids:</p><pre>[3, 2, 1, 5, 6, 4]</pre><p>You need to find the second-highest bid.</p><p>One approach would be to sort every bid and then select the second element.</p><p>But do you really need to sort every bid if you only care about the top two?</p><p>No!</p><p>We can maintain just the two highest bids using a Min Heap of size 2.</p><h3>Step-by-Step Example</h3><p>Here, k = 2.</p><p>Initially, the heap is empty.</p><p><strong>Bid 1: 3</strong></p><pre>Heap: [3]</pre><p><strong>Bid 2: 2</strong></p><pre>Heap: [2, 3]</pre><p>We have the two largest bids so far. The smallest of them is 2.</p><p><strong>Bid 3: 1</strong></p><pre>Insert 1 → [1, 3, 2]<br>Remove the smallest → [2, 3]</pre><p>The bid 1 is too small to remain in the top two.</p><p><strong>Bid 4: 5</strong></p><pre>Insert 5 → [2, 3, 5]<br>Remove the smallest → [3, 5]</pre><p><strong>Bid 5: 6</strong></p><pre>Insert 6 → [3, 5, 6]<br>Remove the smallest → [5, 6]</pre><p><strong>Bid 6: 4</strong></p><pre>Insert 4 → [4, 5, 6]<br>Remove the smallest → [5, 6]</pre><p>The final two largest bids are 5 and 6.</p><p>The smallest of these two is 5, so the second-largest bid is 5.</p><p><strong>Final Answer: 5</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IHVrHhaW07yYG4y8YEBV2g.png" /></figure><h3>Complexity</h3><ul><li><strong>Time:</strong> O(n log k)</li><li><strong>Auxiliary space:</strong> O(k)</li></ul><h3>My Revision Milestone</h3><p>I solved this problem in <strong>under 5 minutes</strong> during my Heap revision.</p><p>For me, the important part wasn’t just writing the code quickly. It was recognizing the pattern immediately:</p><p><strong>Kth Largest → Min Heap of Size K.</strong></p><p>When the pattern becomes familiar, the solution becomes much more straightforward.</p><h3>Quick Revision Rules</h3><ul><li>Need the maximum repeatedly? Use a Max Heap.</li><li>Need the minimum repeatedly? Use a Min Heap.</li><li>Need the K largest elements? Use a Min Heap of size K.</li><li>Need the K smallest elements? Use a Max Heap of size K.</li><li>Need to process incoming values? Consider maintaining a bounded heap.</li></ul><h3>Conclusion</h3><p>Revising Heap today reinforced an important lesson: <strong>DSA is not just about memorizing solutions; it’s about recognizing patterns.</strong></p><p>Starting with Heap fundamentals and applying them to three LeetCode problems helped me connect the concepts with real interview-style questions.</p><p>I’ll continue practicing more Heap patterns, including Top K Frequent Elements, K Closest Points, Merge K Sorted Lists, and Median from Data Stream.</p><p>One step at a time, one pattern at a time. 🚀</p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#BasicsOfHeap #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=694fbee9c27a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Greedy Algorithm : Three Problems That Taught Me to Think Beyond Brute Force]]></title>
            <link>https://medium.com/@harshjaiswal2707/greedy-algorithm-three-problems-that-taught-me-to-think-beyond-brute-force-4dbd4267aba1?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/4dbd4267aba1</guid>
            <category><![CDATA[scheduling]]></category>
            <category><![CDATA[sad]]></category>
            <category><![CDATA[greedy-algorithms]]></category>
            <category><![CDATA[interviewproblem]]></category>
            <category><![CDATA[greedy-allocation]]></category>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 02:23:47 GMT</pubDate>
            <atom:updated>2026-10-09T02:23:47.913Z</atom:updated>
            <content:encoded><![CDATA[<p>Solving a LeetCode problem is not just about finding a solution that passes all test cases. The real learning happens when we understand <em>why</em> an approach works, which decisions we can make greedily, and how to prove that those decisions lead to the correct answer.</p><p>Recently, I solved three interesting LeetCode problems:</p><ul><li><strong>316. Remove Duplicate Letters</strong> — Stack + Greedy</li><li><strong>621. Task Scheduler</strong> — Frequency Counting + Greedy Scheduling</li><li><strong>135. Candy</strong> — Greedy + Two-Pass Traversal</li></ul><p>Although these problems look different, each teaches an important lesson about making optimal decisions.</p><p>Let’s understand them one by one.</p><h3>1. LeetCode 316 — Remove Duplicate Letters</h3><p><strong>Difficulty:</strong> Medium</p><p><strong>Pattern:</strong> Monotonic Stack + Greedy</p><h3>Problem Statement</h3><p>Given a string s, remove duplicate letters so that every letter appears exactly once. The resulting string must be the smallest possible in lexicographical order among all valid answers.</p><p>For example:</p><pre>Input:  s = &quot;bcabc&quot;<br>Output: &quot;abc&quot;</pre><p>Another example:</p><pre>Input:  s = &quot;cbacdcbc&quot;<br>Output: &quot;acdb&quot;</pre><p>Notice that simply removing duplicate characters is not enough. We must also preserve the relative order of the remaining characters and obtain the lexicographically smallest answer.</p><h3>Step 1: Understand the Greedy Intuition</h3><p>Suppose we are building our answer from left to right.</p><p>When we encounter a character, we have two choices:</p><ol><li>Include it if it has not already been included.</li><li>If it is smaller than the last character in our answer, consider removing that last character to make the answer lexicographically smaller.</li></ol><p>But there is an important condition!</p><p><strong>We can remove a character only if it appears again later in the string.</strong></p><p>Otherwise, removing it would mean that the character disappears completely from the answer.</p><p>For example, consider:</p><pre>s = &quot;bcabc&quot;</pre><p>Initially:</p><pre>Answer = &quot;&quot;</pre><p>We process the characters one by one.</p><p>CharacterDecisionStackbAdd bbcAdd cbcaRemove c, then b, because both appear laterabAdd babcAdd cabc</p><p>Final answer:</p><pre>&quot;abc&quot;</pre><p>When we encounter a, both b and c are greater than a. Since both occur again later, we can safely remove them and obtain a smaller answer.</p><p>This is the core greedy decision.</p><h3>Step 2: Why Do We Need a Stack?</h3><p>A stack gives us access to the most recently added character.</p><p>That is exactly what we need because we only want to compare the current character with the last character in our answer.</p><p>We use three things:</p><ul><li>last: stores the last occurrence of every character.</li><li>visited: tells us whether a character is already in the answer.</li><li>stack: maintains our answer while allowing us to remove characters from the end.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gH3kmWb5_5Y0JmUNqo2l9g.png" /></figure><h3>Step 4: Understand the Important Conditions</h3><p><strong>Condition 1:</strong></p><pre>if (visited[ch - &#39;a&#39;])<br>    continue;</pre><p>If the character is already present in our answer, we skip it. Every character must appear exactly once.</p><p><strong>Condition 2:</strong></p><pre>st.back() &gt; ch</pre><p>The last character in the stack is greater than the current character. Replacing that larger character with the smaller current character can improve lexicographical order.</p><p><strong>Condition 3:</strong></p><pre>last[st.back() - &#39;a&#39;] &gt; i</pre><p>The character at the top of the stack occurs again later. Therefore, removing it now will not cause us to lose that character permanently.</p><p><strong>Why reset </strong><strong>visited?</strong></p><pre>visited[st.back() - &#39;a&#39;] = false;</pre><p>Once a character is removed from the stack, it must be allowed to enter the answer again when we encounter it later.</p><p>Without resetting visited, the algorithm could incorrectly skip it.</p><h3>Complexity Analysis</h3><ul><li><strong>Time Complexity:</strong> O(n), because each character is pushed onto and popped from the stack at most once.</li><li><strong>Space Complexity:</strong> O(1) auxiliary space, excluding the output string, because the alphabet contains only 26 lowercase English letters.</li></ul><h3>Key Takeaway</h3><p>The lesson from this problem is that greedy decisions sometimes require future information.</p><p>We use last-occurrence indices to determine whether removing a character is safe, and a monotonic stack to maintain the smallest possible answer.</p><h3>2. LeetCode 621 — Task Scheduler</h3><p><strong>Difficulty:</strong> Medium</p><p><strong>Pattern:</strong> Frequency Counting + Greedy Scheduling</p><h3>Problem Statement</h3><p>We are given a list of CPU tasks represented by capital letters and an integer n, representing the cooldown period.</p><p>The CPU can execute one task per interval. After executing a task, the same task cannot be executed again until at least n intervals have passed.</p><p>During these intervals, the CPU can execute other tasks or remain idle.</p><p>We need to return the minimum number of intervals required to execute all tasks.</p><p>Example:</p><pre>Input:<br>tasks = [&quot;A&quot;,&quot;A&quot;,&quot;A&quot;,&quot;B&quot;,&quot;B&quot;,&quot;B&quot;]<br>n = 2</pre><pre>Output: 8</pre><p>One valid schedule is:</p><pre>A B idle A B idle A B</pre><p>The total is eight intervals.</p><h3>Step 1: Why Is This a Greedy Problem?</h3><p>Suppose task A occurs three times and task B also occurs three times.</p><p>If we execute all the A tasks together, we violate the cooldown requirement.</p><p>Instead, we should prioritize the most frequent tasks and distribute their occurrences across the schedule.</p><p>The key observation is:</p><p><strong>The task that occurs most frequently determines how much space the schedule may need.</strong></p><p>Other tasks can often fill the cooldown gaps.</p><h3>Step 2: Calculate the Frequency</h3><p>For the example:</p><pre>A = 3<br>B = 3</pre><p>The maximum frequency is:</p><pre>maxFreq = 3</pre><p>The number of tasks that share this maximum frequency is:</p><pre>countMax = 2</pre><p>We can calculate the minimum schedule length using:</p><p>intervals=(maxFreq−1)(n+1)+countMax\text{intervals} = (\text{maxFreq}-1)(n+1)+\text{countMax}</p><p>Substituting our values:</p><p>(3−1)(2+1)+2(3–1)(2+1)+2 =2×3+2=8=2\times3+2=8</p><p>Therefore, the answer is 8.</p><h3>Step 3: Understand the Formula</h3><p>Consider the most frequent task, A.</p><p>It appears three times, so we have two gaps between its occurrences:</p><pre>A _ _ A _ _ A</pre><p>Each gap must contain at least n = 2 intervals.</p><p>The first two occurrences create the repeating blocks:</p><pre>A _ _<br>A _ _<br>A</pre><p>The number of complete blocks before the final occurrence is:</p><pre>maxFreq - 1</pre><p>Each block needs:</p><pre>n + 1</pre><p>intervals: one task followed by n cooldown intervals.</p><p>Finally, we add countMax because all tasks tied for the maximum frequency can occupy the final block.</p><p>This gives us:</p><p>(maxFreq−1)(n+1)+countMax(\text{maxFreq}-1)(n+1)+\text{countMax}</p><p>However, this formula is not always the final answer.</p><p>If there are many different tasks and the CPU can execute them without needing idle intervals, the total number of tasks may be larger than this formula.</p><p>Therefore, the complete answer is:</p><p>max⁡(N, (maxFreq−1)(n+1)+countMax)\boxed{ \max\left( N,\, (\text{maxFreq}-1)(n+1)+\text{countMax} \right) }</p><p>Here, NN represents the total number of tasks.</p><h3>Step 4: C++ Solution</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c6EjmuWDq0g7oi9TKTtreg.png" /></figure><h3>Step 5: Dry Run</h3><p>For:</p><pre>tasks = [&quot;A&quot;,&quot;A&quot;,&quot;A&quot;,&quot;B&quot;,&quot;B&quot;,&quot;B&quot;]<br>n = 2</pre><p>Frequency array:</p><pre>A = 3<br>B = 3</pre><p>After sorting, the maximum frequency is 3.</p><pre>maxFreq = 3<br>countMax = 2</pre><p>Calculate:</p><pre>result = (3 - 1) * (2 + 1) + 2<br>       = 2 * 3 + 2<br>       = 8</pre><p>Total number of tasks:</p><pre>tasks.size() = 6</pre><p>Finally:</p><pre>max(6, 8) = 8</pre><p>Answer:</p><pre>8</pre><h3>What If the Cooldown Is Zero?</h3><p>If n = 0, there is no restriction on repeating tasks. The minimum number of intervals is simply the number of tasks.</p><p>The max() in our formula handles this case too.</p><h3>Complexity Analysis</h3><ul><li><strong>Time Complexity:</strong> O(N + 26 log 26), which simplifies to O(N), because there are only 26 possible task types.</li><li><strong>Space Complexity:</strong> O(1) auxiliary space, because the frequency array has a fixed size of 26.</li></ul><h3>Key Takeaway</h3><p>The important lesson from Task Scheduler is that we do not always need to simulate every interval.</p><p>By counting frequencies and identifying the most frequent task, we can derive a mathematical formula for the optimal schedule.</p><h3>3. LeetCode 135 — Candy</h3><p><strong>Difficulty:</strong> Hard</p><p><strong>Pattern:</strong> Greedy + Two-Pass Traversal</p><h3>Problem Statement</h3><p>There are n children standing in a line. Each child has a rating.</p><p>We must distribute candies according to two rules:</p><ol><li>Every child must receive at least one candy.</li><li>A child with a higher rating than an adjacent child must receive more candies than that neighbor.</li></ol><p>Our objective is to minimize the total number of candies distributed.</p><p>Example:</p><pre>Input:<br>ratings = [1, 0, 2]</pre><pre>Output: 5</pre><p>One valid distribution is:</p><pre>Ratings: [1, 0, 2]<br>Candies: [2, 1, 2]</pre><p>Total:</p><pre>2 + 1 + 2 = 5</pre><h3>Step 1: Start With One Candy Per Child</h3><p>Every child must receive at least one candy.</p><p>Therefore, our initial distribution is:</p><pre>Ratings: [1, 0, 2]<br>Candies: [1, 1, 1]</pre><p>Now we must adjust the candies to satisfy the rating conditions while keeping the total as small as possible.</p><h3>Step 2: Why Is One Pass Not Enough?</h3><p>Consider:</p><pre>ratings = [1, 2, 3, 4]</pre><p>A child with a higher rating than their left neighbor must receive more candy.</p><p>The distribution must become:</p><pre>[1, 2, 3, 4]</pre><p>Now consider the reverse:</p><pre>ratings = [4, 3, 2, 1]</pre><p>The distribution must be:</p><pre>[4, 3, 2, 1]</pre><p>A left-to-right pass handles increasing ratings, but it cannot fully handle decreasing sequences.</p><p>That is why we need <strong>two passes</strong>.</p><ul><li>Left to right: satisfy the condition with the left neighbor.</li><li>Right to left: satisfy the condition with the right neighbor.</li></ul><h3>Step 3: First Pass — Left to Right</h3><p>Initialize:</p><pre>vector&lt;int&gt; candy(n, 1);</pre><p>Now compare each child with the child immediately to their left.</p><p>If the current rating is higher, give the current child one more candy than the previous child.</p><pre>for (int i = 1; i &lt; n; i++) {<br>    if (ratings[i] &gt; ratings[i - 1]) {<br>        candy[i] = candy[i - 1] + 1;<br>    }<br>}</pre><p>For:</p><pre>ratings = [1, 0, 2]</pre><p>The ratings increase only from 0 to 2.</p><p>After the first pass:</p><pre>Candies = [1, 1, 2]</pre><p>This satisfies the left-neighbor condition, but we still need to check the right-neighbor condition.</p><h3>Step 4: Second Pass — Right to Left</h3><p>Now move from right to left.</p><p>If a child has a higher rating than the child to their right, they must receive more candies.</p><p>However, we must not reduce the candy count already assigned during the first pass.</p><p>Therefore, we use:</p><pre>for (int i = n - 2; i &gt;= 0; i--) {<br>    if (ratings[i] &gt; ratings[i + 1]) {<br>        candy[i] = max(candy[i], candy[i + 1] + 1);<br>    }<br>}</pre><p>Why max()?</p><p>Suppose the first pass has already assigned three candies to a child. The second pass might require only two.</p><p>We cannot reduce the count to two, because that could violate the condition with the left neighbor.</p><p>So we keep whichever value is larger:</p><pre>max(currentCandies, rightNeighborCandies + 1)</pre><p>After the second pass, our example becomes:</p><pre>Ratings: [1, 0, 2]<br>Candies: [2, 1, 2]</pre><p>The total is:</p><pre>2 + 1 + 2 = 5</pre><h3>Step 5: C++ Solution</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Q6kY8kNF8QLLZwXap6r2ng.png" /></figure><h3>Step 6: Dry Run</h3><p>Let’s use a more interesting example:</p><pre>ratings = [1, 2, 2]</pre><p>Initially:</p><pre>candies = [1, 1, 1]</pre><p>After the left-to-right pass:</p><ul><li>Rating 2 is higher than rating 1, so the second child receives two candies.</li><li>The third child has the same rating as the second child, so no increase is needed.</li></ul><pre>candies = [1, 2, 1]</pre><p>During the right-to-left pass:</p><ul><li>The third child’s rating is not higher than the second child’s rating.</li><li>No further adjustment is necessary.</li></ul><p>Final distribution:</p><pre>[1, 2, 1]</pre><p>Total:</p><pre>1 + 2 + 1 = 4</pre><p>Notice that equal ratings do not require different candy counts. The requirement applies only when one child’s rating is strictly greater than an adjacent child’s rating.</p><h3>Complexity Analysis</h3><ul><li><strong>Time Complexity:</strong> O(n), because we make two passes through the ratings and one pass to calculate the total.</li><li><strong>Space Complexity:</strong> O(n), because we maintain a candy array.</li></ul><h3>Key Takeaway</h3><p>The Candy problem teaches us that a greedy algorithm may need multiple passes when a decision depends on constraints in both directions.</p><p>The first pass handles left-side relationships, and the second pass fixes right-side relationships without undoing previous requirements.</p><h3>What These Three Problems Taught Me</h3><p>These problems strengthened my understanding of three different patterns.</p><p>ProblemMain PatternCore Insight</p><p>LC 316 — Remove Duplicate LettersMonotonic Stack + GreedyRemove a larger character only when it can safely appear again later.</p><p>LC 621 — Task SchedulerFrequency Counting + GreedyThe most frequent tasks determine the minimum schedule length.</p><p>LC 135 — CandyTwo-Pass GreedySatisfy constraints from both directions without reducing previously required candy counts.</p><h3>Final Thoughts</h3><p>One of the most important lessons I am learning through DSA is that different problems require different ways of thinking.</p><ul><li><strong>Stacks</strong> help us maintain an optimal partial answer.</li><li><strong>Frequency counting</strong> helps us identify the elements that dominate a problem.</li><li><strong>Multiple greedy passes</strong> help us satisfy constraints that depend on both sides of an element.</li></ul><p>Instead of memorizing solutions, I am focusing on understanding the reasoning behind them, identifying the correct pattern, and knowing why the algorithm works.</p><p>The goal is not just to solve more problems. It is to become better at recognizing the right approach when facing a completely new problem.</p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#GreedyAllocation #Scheduling #GreedyAlgorithm #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4dbd4267aba1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Two Pointers, Differences & Greedy Reconstruction — Greedy Algorithms]]></title>
            <link>https://medium.com/@harshjaiswal2707/two-pointers-differences-greedy-reconstruction-greedy-algorithms-49be9ba1e2e9?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/49be9ba1e2e9</guid>
            <category><![CDATA[difference]]></category>
            <category><![CDATA[two-pointers]]></category>
            <category><![CDATA[sad]]></category>
            <category><![CDATA[greedy-reconstruction]]></category>
            <category><![CDATA[greedy-algorithms]]></category>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Tue, 06 Oct 2026 03:52:42 GMT</pubDate>
            <atom:updated>2026-10-06T03:52:42.430Z</atom:updated>
            <content:encoded><![CDATA[<p>After completing the basic Greedy problems, I moved to the next phase where Greedy starts becoming a little more interesting.</p><p>In this phase, I solved four problems:</p><ul><li><strong>LeetCode 881 — Boats to Save People</strong></li><li><strong>LeetCode 1029 — Two City Scheduling</strong></li><li><strong>LeetCode 406 — Queue Reconstruction by Height</strong></li><li><strong>LeetCode 646 — Maximum Length of Pair Chain</strong></li></ul><p>These problems may look completely different, but they teach four very useful Greedy patterns:</p><blockquote><strong><em>Sort + Two Pointers</em></strong><em><br></em><strong><em>Sort by Difference</em></strong><em><br></em><strong><em>Greedy Reconstruction</em></strong><em><br></em><strong><em>Earliest Ending / Interval Scheduling</em></strong></blockquote><p>The main goal of this phase was not just to solve these four problems, but to understand <strong>why a particular greedy choice works</strong>.</p><h3>1. LeetCode 881 — Boats to Save People</h3><h3>Problem</h3><p>We are given an array people, where people[i] represents the weight of a person.</p><p>We also have a boat with a maximum weight limit.</p><p>Each boat can carry <strong>at most two people</strong>.</p><p>We need to find the <strong>minimum number of boats</strong> required to rescue everyone.</p><h3>Example</h3><pre>people = [3, 2, 2, 1]<br>limit = 3</pre><p>We can arrange them as:</p><pre>1 + 2 = 3<br>3<br>2</pre><p>So the answer is:</p><pre>3 boats</pre><h3>The Greedy Idea</h3><p>At first, we might try many combinations.</p><p>But there is a simple observation:</p><p>If the <strong>heaviest person</strong> can go with the <strong>lightest person</strong>, we should pair them.</p><p>Why?</p><p>Because the heaviest person is the hardest person to accommodate.</p><p>So we sort the array:</p><pre>[1, 2, 2, 3]</pre><p>Then use two pointers:</p><pre>left  → lightest<br>right → heaviest</pre><p>Now check:</p><pre>people[left] + people[right]</pre><p>If their total weight is within the limit:</p><pre>left + right &lt;= limit</pre><p>we put both in one boat.</p><p>Otherwise, the heaviest person must go alone.</p><h3>Example</h3><pre>[1, 2, 2, 3]<br>limit = 3</pre><h3>Step 1</h3><pre>1 + 3 = 4</pre><p>Too heavy.</p><p>So 3 must go alone.</p><pre>Boat 1 → [3]</pre><p>Move right.</p><h3>Step 2</h3><p>Now:</p><pre>1 + 2 = 3</pre><p>They can share.</p><pre>Boat 2 → [1,2]</pre><p>Move both pointers.</p><h3>Step 3</h3><p>Remaining:</p><pre>[2]</pre><p>So:</p><pre>Boat 3 → [2]</pre><p>Answer:</p><pre>3</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2iF-3YKYK6I_BaSs9XffiQ.png" /></figure><h3>Pattern</h3><h3>Sort + Two Pointers</h3><p>The general template is:</p><pre>Sort<br> ↓<br>left = 0<br>right = n - 1<br> ↓<br>Check smallest + largest<br> ↓<br>If possible → use them together<br>Otherwise → largest goes alone</pre><h3>Key Greedy Decision</h3><blockquote><strong><em>Always try to accommodate the heaviest person with the lightest person.</em></strong></blockquote><p>This problem is a good example of how <strong>sorting + two pointers can turn a complicated pairing problem into a simple greedy process.</strong></p><h3>2. LeetCode 1029 — Two City Scheduling</h3><h3>Problem</h3><p>We have 2n people.</p><p>Every person can go to either:</p><ul><li>City A</li><li>City B</li></ul><p>Each person has a different cost for both cities.</p><p>We need to send exactly:</p><pre>n people → City A<br>n people → City B</pre><p>while minimizing the total cost.</p><h3>Example</h3><p>Suppose:</p><pre>[10, 20]<br>[30, 200]<br>[400, 50]<br>[30, 20]</pre><p>For each person:</p><pre>Person 1 → A = 10,  B = 20<br>Person 2 → A = 30,  B = 200<br>Person 3 → A = 400, B = 50<br>Person 4 → A = 30,  B = 20</pre><p>We need:</p><pre>2 people → A<br>2 people → B</pre><h3>The Important Observation</h3><p>Instead of looking at the costs separately, calculate:</p><pre>A - B</pre><p>For example:</p><pre>Person 1:<br>10 - 20 = -10</pre><pre>Person 2:<br>30 - 200 = -170</pre><pre>Person 3:<br>400 - 50 = 350</pre><pre>Person 4:<br>30 - 20 = 10</pre><p>Now the difference tells us something useful.</p><h3>Negative difference</h3><pre>A - B &lt; 0</pre><p>means City A is cheaper.</p><h3>Positive difference</h3><pre>A - B &gt; 0</pre><p>means City B is cheaper.</p><p>So we sort people based on this difference.</p><h3>Why Does Sorting by Difference Work?</h3><p>Suppose someone’s difference is:</p><pre>-170</pre><p>That means sending them to A instead of B saves a lot of money.</p><p>While someone with:</p><pre>350</pre><p>strongly prefers B.</p><p>Therefore, we want the people with the <strong>smallest differences</strong> to go to A.</p><p>And the people with the <strong>largest differences</strong> to go to B.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8X1p_KUgJhtRwYW0PDxk8w.png" /></figure><h3>Pattern</h3><h3>Sort by Cost Difference</h3><pre>difference = costA - costB</pre><p>Then:</p><pre>First n people → City A<br>Remaining n people → City B</pre><p>This works because the sorted difference tells us <strong>which assignment gives us the biggest advantage</strong>.</p><h3>Key Lesson</h3><p>This problem teaches an important Greedy technique:</p><blockquote><em>Sometimes we should not sort using the original values.<br>We should sort using the </em><strong><em>difference between two choices</em></strong><em>.</em></blockquote><p>Whenever a problem gives you:</p><pre>Choice A → cost/value<br>Choice B → cost/value</pre><p>you should think:</p><pre>What happens if I choose A instead of B?</pre><p>That difference can become the greedy criterion.</p><h3>3. LeetCode 406 — Queue Reconstruction by Height</h3><p>This problem is slightly different from the previous two.</p><p>Here, we don’t just select people.</p><p>We have to <strong>construct the final answer</strong>.</p><h3>Problem</h3><p>Each person is represented as:</p><pre>[h, k]</pre><p>where:</p><pre>h = height<br>k = number of people in front of them who have height &gt;= h</pre><p>We need to reconstruct the queue.</p><h3>Example</h3><p>Suppose we have:</p><pre>[7,0]<br>[4,4]<br>[7,1]<br>[5,0]<br>[6,1]<br>[5,2]</pre><p>We need to arrange them so that every person’s k condition is satisfied.</p><p>At first, this looks difficult because inserting one person can affect the conditions of other people.</p><h3>The Greedy Trick</h3><p>We sort people by:</p><h3>First:</h3><p>Higher height first.</p><h3>If heights are equal:</h3><p>Smaller k first.</p><p>So:</p><pre>[7,0]<br>[7,1]<br>[6,1]<br>[5,0]<br>[5,2]<br>[4,4]</pre><p>Then we insert each person at index k.</p><h3>Why Taller People First?</h3><p>This is the most important part of the problem.</p><p>Suppose we have already placed taller people.</p><p>Now we insert a shorter person.</p><p>That shorter person <strong>cannot affect the </strong><strong>k count of taller people</strong>, because the taller people only care about people who are at least as tall as themselves.</p><p>Therefore:</p><blockquote><em>Place taller people first, then insert shorter people.</em></blockquote><p>This makes the problem much easier.</p><h3>Step-by-Step</h3><p>Start:</p><pre>[]</pre><p>Insert:</p><pre>[7,0]</pre><p>Queue:</p><pre>[7,0]</pre><p>Next:</p><pre>[7,1]</pre><p>Insert at index 1:</p><pre>[7,0] [7,1]</pre><p>Next:</p><pre>[6,1]</pre><p>Insert at index 1:</p><pre>[7,0] [6,1] [7,1]</pre><p>Continue doing the same for every person.</p><p>The final queue becomes:</p><pre>[5,0]<br>[7,0]<br>[5,2]<br>[6,1]<br>[4,4]<br>[7,1]</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p5ULwJWb_YImAlJsQH18LQ.png" /></figure><h3>Pattern</h3><h3>Custom Sorting + Insertion</h3><p>The general idea is:</p><pre>Sort by a property<br>       ↓<br>Process important elements first<br>       ↓<br>Insert each element at its required position</pre><p>The important observation is:</p><blockquote><strong><em>The larger elements should be fixed first because smaller elements cannot invalidate their conditions.</em></strong></blockquote><h3>Key Lesson</h3><p>This problem teaches a very useful Greedy concept:</p><h3>Greedy Reconstruction</h3><p>Sometimes the answer isn’t found by simply selecting elements.</p><p>Instead:</p><pre>Choose the correct processing order<br>        ↓<br>Build the answer step-by-step</pre><p>This pattern appears in many interview problems where we need to <strong>construct a valid arrangement</strong>.</p><h3>4. LeetCode 646 — Maximum Length of Pair Chain</h3><p>Now we come to another important Greedy pattern.</p><p>This problem is closely related to <strong>Interval Scheduling</strong>.</p><h3>Problem</h3><p>We are given pairs:</p><pre>[a, b]</pre><p>We can put pair [c,d] after [a,b] if:</p><pre>b &lt; c</pre><p>We need to find the <strong>maximum number of pairs</strong> that can form a chain.</p><h3>Example</h3><pre>pairs =<br>[[1,2],<br> [2,3],<br> [3,4]]</pre><p>We can choose:</p><pre>[1,2] → [3,4]</pre><p>because:</p><pre>2 &lt; 3</pre><p>So the answer is:</p><pre>2</pre><h3>The Greedy Idea</h3><p>Suppose we have two possible pairs:</p><pre>[1,10]<br>[2,3]</pre><p>Which one should we choose?</p><p>At first, [1,10] starts earlier.</p><p>But choosing it is bad because it ends at 10.</p><p>Choosing:</p><pre>[2,3]</pre><p>ends much earlier.</p><p>That leaves more space for future pairs.</p><p>Therefore:</p><blockquote><strong><em>Always prefer the pair that finishes earliest.</em></strong></blockquote><h3>Example</h3><p>Consider:</p><pre>[1,2]<br>[2,3]<br>[3,4]<br>[5,6]</pre><p>Sort by ending value:</p><pre>[1,2]<br>[2,3]<br>[3,4]<br>[5,6]</pre><p>Start with:</p><pre>end = -∞</pre><p>Choose:</p><pre>[1,2]</pre><p>Now:</p><pre>end = 2<br>count = 1</pre><p>Next:</p><pre>[2,3]</pre><p>Condition:</p><pre>2 &lt; 2</pre><p>False.</p><p>So skip it.</p><p>Next:</p><pre>[3,4]</pre><pre>2 &lt; 3</pre><p>True.</p><p>Choose it:</p><pre>end = 4<br>count = 2</pre><p>Next:</p><pre>[5,6]</pre><pre>4 &lt; 5</pre><p>True.</p><p>Choose it:</p><pre>count = 3</pre><p>Answer:</p><pre>3</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*twRunrniR2t-4W6jKJNJTQ.png" /></figure><h3>Pattern</h3><h3>Earliest Ending</h3><p>The general pattern is:</p><pre>Sort by ending point<br>        ↓<br>Choose the pair that finishes earliest<br>        ↓<br>Skip overlapping pairs<br>        ↓<br>Continue</pre><p>Why?</p><p>Because:</p><blockquote><strong><em>The earlier we finish, the more choices we have for the future.</em></strong></blockquote><p>This is the same fundamental idea used in the classic <strong>Activity Selection / Interval Scheduling</strong> problem.</p><h3>🔥 How to Identify These Patterns in Interviews?</h3><p>When you see a new Greedy problem, don’t immediately start coding.</p><p>First ask:</p><h3>1. Can sorting simplify the problem?</h3><p>If yes, try:</p><pre>Sort → Greedy</pre><h3>2. Are we pairing smallest and largest elements?</h3><p>Think:</p><pre>Two Pointers</pre><p>Like:</p><pre>LC 881</pre><h3>3. Are there two choices with different costs?</h3><p>Try calculating:</p><pre>costA - costB</pre><p>Like:</p><pre>LC 1029</pre><h3>4. Do we need to construct an arrangement?</h3><p>Think:</p><pre>Sort → Process in the correct order → Insert</pre><p>Like:</p><pre>LC 406</pre><h3>5. Are we selecting non-overlapping intervals/pairs?</h3><p>Think:</p><pre>Sort by ending point</pre><p>Like:</p><pre>LC 646</pre><h3>🚀 Final Takeaway</h3><p>The biggest lesson from Phase 4 was:</p><blockquote><strong><em>Greedy is about finding the right ordering and making a choice that leaves the best possible future.</em></strong></blockquote><p>The four problems taught me four different ways to do that:</p><pre>LC 881<br>↓<br>Use two pointers to make the best pairing</pre><pre>LC 1029<br>↓<br>Use differences to compare choices</pre><pre>LC 406<br>↓<br>Construct the answer in a safe greedy order</pre><pre>LC 646<br>↓<br>Finish as early as possible to maximize future choices</pre><p>So whenever I see a Greedy problem now, my first questions are:</p><pre>Can I sort it?</pre><pre>What should I sort by?</pre><pre>What local choice gives me the best future?</pre><pre>Can I prove that this choice doesn&#39;t hurt the remaining options?</pre><p>That mindset is much more important than memorizing individual Greedy solutions.</p><h3>Problems Covered</h3><p>🔹 <strong>LC 881 — Boats to Save People</strong><br>🔹 <strong>LC 1029 — Two City Scheduling</strong><br>🔹 <strong>LC 406 — Queue Reconstruction by Height</strong><br>🔹 <strong>LC 646 — Maximum Length of Pair Chain</strong></p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#TwoPointers #Differences #GreedyReconstruction #Greedy Algorithms #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=49be9ba1e2e9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ Reachability & Greedy Boundaries — A Powerful Greedy Pattern]]></title>
            <link>https://medium.com/@harshjaiswal2707/reachability-greedy-boundaries-a-powerful-greedy-pattern-35274f5a4d8c?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/35274f5a4d8c</guid>
            <category><![CDATA[greedypattern]]></category>
            <category><![CDATA[greedyboundaries]]></category>
            <category><![CDATA[sad]]></category>
            <category><![CDATA[reachabilityboundaries]]></category>
            <category><![CDATA[interviewproblem]]></category>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Wed, 30 Sep 2026 02:31:20 GMT</pubDate>
            <atom:updated>2026-09-30T02:31:20.858Z</atom:updated>
            <content:encoded><![CDATA[<p>While learning Greedy Algorithms, I completed four important problems:</p><ul><li><strong>LC 55 — Jump Game</strong></li><li><strong>LC 45 — Jump Game II</strong></li><li><strong>LC 134 — Gas Station</strong></li><li><strong>LC 763 — Partition Labels</strong></li></ul><p>At first, these problems look completely different.</p><p>One is about jumping, one is about minimum jumps, one is about a circular gas route, and one is about splitting a string.</p><p>But they share an important idea:</p><blockquote><strong><em>Instead of exploring every possibility, maintain the best boundary/reachable state and make a greedy decision when the current boundary is exhausted or becomes invalid.</em></strong></blockquote><p>This is the <strong>Reachability &amp; Greedy Boundaries</strong> pattern.</p><h3>🧠 What is Reachability &amp; Greedy Boundaries?</h3><p>In many greedy problems, we don’t need to know every possible path.</p><p>We only need to maintain something like:</p><ul><li><strong>How far can I reach?</strong></li><li><strong>What is the current valid range?</strong></li><li><strong>Where can the next boundary be?</strong></li><li><strong>When should I discard the current candidate?</strong></li></ul><p>The important thing is that we continuously maintain the <strong>best possible boundary</strong>.</p><p>For example:</p><pre>Current Range<br>[0 ----------- farthest]<br>                  ↑<br>            best reachable point</pre><p>As we process more elements, we expand this boundary.</p><p>If the current boundary becomes impossible or fully explored, we make a greedy decision.</p><h3>1️⃣ LC 55 — Jump Game</h3><h3>Problem</h3><p>You are given an array where:</p><pre>nums[i] = maximum jump length from index i</pre><p>You need to determine whether you can reach the last index.</p><p>Example:</p><pre>nums = [2,3,1,1,4]</pre><p>From index 0:</p><pre>0 → 1 or 2</pre><p>From index 1:</p><pre>1 → 2, 3 or 4</pre><p>So we can reach the last index.</p><p>Answer:</p><pre>true</pre><h3>💡 Greedy Idea</h3><p>We don’t need to try every jump.</p><p>We simply maintain:</p><pre>farthest</pre><p>which represents the <strong>farthest index we can currently reach</strong>.</p><pre>farthest = max(farthest, i + nums[i]);</pre><p>At every index:</p><pre>Current index + maximum jump</pre><p>gives us a possible new boundary.</p><p>Example:</p><pre>[2, 3, 1, 1, 4]<br> ↑<br> i = 0</pre><pre>farthest = 0 + 2 = 2</pre><p>At index 1:</p><pre>farthest = max(2, 1 + 3)<br>         = 4</pre><p>Now the last index is reachable.</p><h3>Core Pattern</h3><pre>Maintain the farthest reachable position.</pre><p>This is a <strong>reachability problem</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XWG17raczk6BFJ9UONUtCw.png" /></figure><h3>Complexity</h3><pre>Time:  O(n)<br>Space: O(1)</pre><h3>2️⃣ LC 45 — Jump Game II</h3><p>LC 55 asks:</p><blockquote><em>Can I reach the end?</em></blockquote><p>LC 45 asks a harder question:</p><blockquote><em>What is the minimum number of jumps required to reach the end?</em></blockquote><p>Example:</p><pre>nums = [2,3,1,1,4]</pre><p>Answer:</p><pre>2</pre><p>Because:</p><pre>0 → 1 → 4</pre><h3>💡 Greedy Idea — Reachable Range</h3><p>Here we maintain two important boundaries:</p><pre>currEnd<br>farthest</pre><h3>currEnd</h3><p>The farthest position reachable using the <strong>current number of jumps</strong>.</p><h3>farthest</h3><p>The farthest position we can reach using <strong>one more jump</strong> from the current range.</p><p>Example:</p><pre>[2, 3, 1, 1, 4]<br> 0  1  2  3  4</pre><p>Initially:</p><pre>currEnd = 0<br>farthest = 0<br>jump = 0</pre><p>At index 0:</p><pre>farthest = 2</pre><p>We have reached the end of our current range:</p><pre>i == currEnd</pre><p>So we must take another jump:</p><pre>jump = 1<br>currEnd = farthest</pre><p>Now:</p><pre>Current reachable range:</pre><pre>[0 ----- 2]</pre><p>We explore indices 1 and 2.</p><p>At index 1:</p><pre>1 + nums[1]<br>= 1 + 3<br>= 4</pre><p>So:</p><pre>farthest = 4</pre><p>When we reach index 2:</p><pre>i == currEnd</pre><p>we take the second jump.</p><pre>jump = 2<br>currEnd = 4</pre><p>We reached the destination.</p><h3>🔑 Important Insight</h3><p>We don’t increment jump at every index.</p><p>We increment it only when:</p><pre>i == currEnd</pre><p>because that means:</p><blockquote><em>“I have completely explored everything reachable with my current number of jumps. Now I need to use the next jump.”</em></blockquote><h3>Core Pattern</h3><pre>Current boundary<br>        ↓<br>Explore entire range<br>        ↓<br>Find farthest next boundary<br>        ↓<br>Take one greedy jump<br>        ↓<br>Repeat</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EsahMgpJxH3vtxgqTZmF8Q.png" /></figure><h3>Complexity</h3><pre>Time:  O(n)<br>Space: O(1)</pre><h3>3️⃣ LC 134 — Gas Station</h3><p>This problem looks completely different.</p><p>You have:</p><pre>gas[i]  → gas available at station i<br>cost[i] → gas required to reach next station</pre><p>You need to find a starting station from which you can complete the circular route.</p><h3>💡 Convert the Problem</h3><p>Instead of thinking separately about gas and cost, calculate:</p><pre>gas[i] - cost[i]</pre><p>This represents the <strong>net gain/loss</strong> at station i.</p><p>Example:</p><pre>gas  =  [2, 3, 4]<br>cost =  [3, 4, 2]</pre><pre>net  = [-1,-1,+2]</pre><p>Now the problem becomes:</p><pre>Can we choose a starting point<br>such that the running balance never becomes negative?</pre><h3>🔥 Greedy Boundary Idea</h3><p>We maintain:</p><pre>currGas</pre><p>This represents the current balance from our selected starting point.</p><p>Whenever:</p><pre>currGas &lt; 0</pre><p>our current starting point cannot work.</p><p>So we discard it and move the starting point:</p><pre>start = i + 1;<br>currGas = 0;</pre><p>We don’t go back and test every previous station.</p><p>That’s the greedy part.</p><h3>Why Can We Skip Previous Candidates?</h3><p>Suppose starting from:</p><pre>start</pre><p>we eventually reach index i and:</p><pre>currGas &lt; 0</pre><p>Then the current starting point fails.</p><p>More importantly, the stations between start and i cannot become a valid starting point either, because the accumulated balance from those candidates to i will also be insufficient under the same prefix-boundary argument.</p><p>Therefore we can safely jump to:</p><pre>i + 1</pre><p>This eliminates unnecessary checking.</p><h3>currGas vs totalGas</h3><p>This was an important part of this problem.</p><h3>currGas</h3><p>Used for <strong>local feasibility</strong>:</p><pre>Can my current starting point survive?</pre><h3>totalGas</h3><p>Used for <strong>global feasibility</strong>:</p><pre>Is there enough gas overall?</pre><p>If:</p><pre>totalGas &lt; 0</pre><p>then:</p><pre>Total gas &lt; Total cost</pre><p>So completing the circuit is impossible from <strong>any</strong> starting point.</p><h3>Core Pattern</h3><pre>Running balance<br>      ↓<br>If balance becomes negative<br>      ↓<br>Current candidate fails<br>      ↓<br>Move boundary to next candidate</pre><p>This is another form of <strong>greedy boundary movement</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*m6lnUKM0qJCiO-6IwlY8dA.png" /></figure><h3>Complexity</h3><pre>Time:  O(n)<br>Space: O(1)</pre><h3>4️⃣ LC 763 — Partition Labels</h3><p>This problem changes the type of boundary.</p><p>Instead of an array of jumps, now we are dealing with a string.</p><h3>Problem</h3><p>Split the string into as many parts as possible such that:</p><blockquote><em>Every character appears in at most one partition.</em></blockquote><p>Example:</p><pre>s = &quot;ababcc&quot;</pre><p>We can split:</p><pre>&quot;abab&quot; | &quot;cc&quot;</pre><p>But:</p><pre>&quot;aba&quot; | &quot;bcc&quot;</pre><p>is invalid because b appears in both parts.</p><h3>💡 Greedy Idea — Last Occurrence Boundary</h3><p>First, store the last occurrence of every character.</p><p>For example:</p><pre>s = &quot;ababcc&quot;</pre><pre>a → 2<br>b → 3<br>c → 5</pre><p>Now start from index 0.</p><p>Character:</p><pre>&#39;a&#39;</pre><p>Its last occurrence is:</p><pre>2</pre><p>So initially:</p><pre>boundary = 2</pre><p>But while processing the partition, we may encounter another character whose last occurrence lies farther away.</p><p>At index 1:</p><pre>b → last occurrence = 3</pre><p>So boundary becomes:</p><pre>boundary = 3</pre><p>Now the partition must extend to index 3.</p><p>We get:</p><pre>&quot;abab&quot;</pre><p>At index 3:</p><pre>i == boundary</pre><p>So we can safely close the partition.</p><p>Then start the next partition:</p><pre>&quot;cc&quot;</pre><p>Final answer:</p><pre>[4, 2]</pre><h3>🔥 The Important Greedy Boundary</h3><p>The boundary keeps expanding:</p><pre>a → boundary = 2</pre><pre>b → boundary = 3</pre><pre>...</pre><p>We only close the partition when:</p><pre>currentIndex == boundary</pre><p>Because at that point, every character seen inside the partition has its <strong>last occurrence inside the partition</strong>.</p><p>Therefore, no character needs to appear in the next partition.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5mDvh_fKB030lUQ0JWAhBQ.png" /></figure><h3>🚀 The Pattern</h3><h3>Step 1 — Maintain the Best Boundary</h3><p>Ask:</p><blockquote><em>“What is the farthest / latest / valid boundary I can currently guarantee?”</em></blockquote><p>Examples:</p><pre>LC 55  → farthest reachable index<br>LC 45  → farthest next range<br>LC 134 → valid starting boundary<br>LC 763 → last occurrence boundary</pre><h3>Step 2 — Expand or Move the Boundary</h3><p>As you process the input, update the boundary.</p><pre>farthest = max(farthest, i + nums[i]);</pre><p>or:</p><pre>end = max(end, lastOccurrence[s[i] - &#39;a&#39;]);</pre><p>or move the candidate:</p><pre>start = i + 1;</pre><h3>Step 3 — Make a Greedy Decision at the Boundary</h3><p>When the current range/boundary is exhausted:</p><pre>LC 45  → take another jump<br>LC 763 → close the partition</pre><p>When the current candidate becomes invalid:</p><pre>LC 134 → discard candidate</pre><h3>🎯 What I Learned</h3><p>The biggest lesson from these problems is:</p><blockquote><strong><em>Greedy is often about maintaining the right boundary rather than exploring every possibility.</em></strong></blockquote><p>Instead of:</p><pre>Try everything<br>    ↓<br>Compare everything<br>    ↓<br>Choose best</pre><p>we try to maintain:</p><pre>Best current state<br>       ↓<br>Expand boundary<br>       ↓<br>Detect when decision is forced<br>       ↓<br>Make greedy decision</pre><p>That is what makes these solutions efficient.</p><h3>📌 Problems Covered</h3><h3>LC 55 — Jump Game</h3><p><strong>Pattern:</strong> Farthest Reachability</p><h3>LC 45 — Jump Game II</h3><p><strong>Pattern:</strong> Greedy Frontier / Minimum Jumps</p><h3>LC 134 — Gas Station</h3><p><strong>Pattern:</strong> Running Balance + Candidate Reset</p><h3>LC 763 — Partition Labels</h3><p><strong>Pattern:</strong> Last Occurrence + Greedy Partition Boundary</p><h3>🚀 Greedy Progress</h3><p>With these four problems, I have covered an important Greedy concept:</p><p><strong>Reachability + Boundary Management</strong></p><p>The goal is not just to solve individual problems, but to recognize the underlying pattern and apply it to new problems.</p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#ReachabilityBoundaries #GreedyBoundaries #GreedyPattern #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=35274f5a4d8c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ Greedy Algorithms: Interval Scheduling & Overlapping Intervals]]></title>
            <link>https://medium.com/@harshjaiswal2707/greedy-algorithms-interval-scheduling-overlapping-intervals-5218d2a6417a?source=rss-cebfcd2e3977------2</link>
            <guid isPermaLink="false">https://medium.com/p/5218d2a6417a</guid>
            <category><![CDATA[interval-scheduling]]></category>
            <category><![CDATA[overlapping-intervals]]></category>
            <category><![CDATA[interviewproblem]]></category>
            <category><![CDATA[sad]]></category>
            <category><![CDATA[greedy-algorithms]]></category>
            <dc:creator><![CDATA[Harsh Jaiswal]]></dc:creator>
            <pubDate>Sat, 26 Sep 2026 03:40:30 GMT</pubDate>
            <atom:updated>2026-09-26T03:40:30.557Z</atom:updated>
            <content:encoded><![CDATA[<p>While learning <strong>Greedy Algorithms</strong>, I came across a pattern that was new for me: <strong>Intervals</strong>.</p><p>At first, intervals looked like a simple 2D array problem, but while solving them, I realized that the main challenge is understanding <strong>how to sort intervals and what decision to make after sorting</strong>.</p><p>I recently solved:</p><ul><li>🔹 <strong>LeetCode 452 — Minimum Number of Arrows to Burst Balloons</strong></li><li>🔹 <strong>LeetCode 57 — Insert Interval</strong></li></ul><p>These problems helped me understand two important interval patterns:</p><ol><li><strong>Interval Scheduling</strong></li><li><strong>Overlapping Intervals</strong></li></ol><h3>🧠 1. Interval Scheduling</h3><p>Interval Scheduling problems usually involve intervals like:</p><pre>[start, end]</pre><p>The important question is often:</p><blockquote><em>“Which interval should I choose first so that I can make the best possible decisions for the remaining intervals?”</em></blockquote><p>A very common greedy idea is:</p><h3>👉 Sort intervals by their ending point</h3><p>Why?</p><p>Because if I choose the interval that <strong>ends earliest</strong>, I leave maximum possible space for the remaining intervals.</p><p>For example:</p><pre>[1,3]<br>[2,5]<br>[4,6]<br>[6,8]</pre><p>If I choose:</p><pre>[1,3]</pre><p>I finish early and can potentially choose more intervals afterward.</p><p>This is the basic idea behind <strong>Interval Scheduling</strong>.</p><h3>General Pattern</h3><pre>Sort intervals by end time<br>        ↓<br>Choose the interval that finishes earliest<br>        ↓<br>Update the boundary<br>        ↓<br>Continue greedily</pre><p>This pattern appears in problems involving:</p><ul><li>Maximum number of non-overlapping intervals</li><li>Minimum resources</li><li>Selecting compatible intervals</li><li>Removing conflicting intervals</li><li>Finding the best boundary</li></ul><h3>🎈 2. Overlapping Intervals</h3><p>The second important pattern is handling <strong>overlapping intervals</strong>.</p><p>Two intervals overlap when their ranges intersect.</p><p>For example:</p><pre>[1,5]<br>   [3,7]</pre><p>They overlap because:</p><pre>3 &lt;= 5</pre><p>A very useful condition is:</p><pre>start &lt;= currentEnd</pre><p>which means the new interval overlaps with the current interval.</p><p>If:</p><pre>start &gt; currentEnd</pre><p>then there is no overlap.</p><h3>🔥 Problem 1 — LeetCode 452</h3><h3>Minimum Number of Arrows to Burst Balloons</h3><h3>Problem</h3><p>We are given balloons represented as intervals:</p><pre>[x_start, x_end]</pre><p>A balloon exists from x_start to x_end.</p><p>An arrow is shot vertically at some x coordinate.</p><p>If:</p><pre>x_start &lt;= x &lt;= x_end</pre><p>the arrow bursts that balloon.</p><p>The goal is:</p><blockquote><em>Find the minimum number of arrows needed to burst all balloons.</em></blockquote><h3>Example</h3><pre>[10,16]<br>[2,8]<br>[1,6]<br>[7,12]</pre><p>We want to burst all balloons with minimum arrows.</p><p>First, sort them by their ending point:</p><pre>[1,6]<br>[2,8]<br>[7,12]<br>[10,16]</pre><p>Now look at the first balloon:</p><pre>[1,6]</pre><p>If we shoot an arrow at:</p><pre>x = 6</pre><p>it bursts:</p><pre>[1,6]<br>[2,8]</pre><p>because both contain 6.</p><p>Now:</p><pre>[7,12]</pre><p>doesn’t contain 6, so we need another arrow.</p><p>Shoot at:</p><pre>x = 12</pre><p>This also bursts:</p><pre>[10,16]</pre><p>So the answer is:</p><pre>2</pre><h3>💡 The Greedy Idea</h3><p>The important observation is:</p><blockquote><em>When intervals overlap, shoot at the </em><strong><em>smallest possible ending point</em></strong><em>.</em></blockquote><p>Why?</p><p>Suppose we have:</p><pre>[1,6]<br>[2,8]</pre><p>Their common region is:</p><pre>[2,6]</pre><p>If we shoot at 6, we burst both.</p><p>Now suppose we have:</p><pre>[1,6]<br>[2,8]<br>[4,10]</pre><p>The common region is:</p><pre>[4,6]</pre><p>Again, shooting at 6 works for all three.</p><p>So we keep track of the <strong>current end boundary</strong>.</p><h3>🔑 Algorithm</h3><h3>Step 1</h3><p>Sort intervals by their end:</p><pre>sort(points.begin(), points.end(),<br>     [](vector&lt;int&gt;&amp; a, vector&lt;int&gt;&amp; b) {<br>         return a[1] &lt; b[1];<br>     });</pre><h3>Step 2</h3><p>Start with one arrow.</p><pre>int arrows = 1;</pre><p>The first arrow is placed at:</p><pre>points[0][1]</pre><h3>Step 3</h3><p>Check every next interval.</p><p>If:</p><pre>points[i][0] &gt; arrowPosition</pre><p>then the current arrow cannot burst this balloon.</p><p>So we need another arrow.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uHLV6GBr5uHJ0w5IrhuC1w.png" /></figure><h3>🧠 Dry Run</h3><p>Input:</p><pre>[10,16]<br>[2,8]<br>[1,6]<br>[7,12]</pre><p>After sorting:</p><pre>[1,6]<br>[2,8]<br>[7,12]<br>[10,16]</pre><p>Initially:</p><pre>arrows = 1<br>arrowPosition = 6</pre><h3>[2,8]</h3><pre>2 &lt;= 6</pre><p>Overlap.</p><p>Same arrow works.</p><h3>[7,12]</h3><pre>7 &gt; 6</pre><p>No overlap.</p><p>Need another arrow.</p><pre>arrows = 2<br>arrowPosition = 12</pre><h3>[10,16]</h3><pre>10 &lt;= 12</pre><p>Overlap.</p><p>Same arrow works.</p><p>Final:</p><pre>2</pre><h3>🔥 Problem 2 — LeetCode 57</h3><h3>Insert Interval</h3><p>This problem is slightly different.</p><p>Here, we are given:</p><pre>intervals</pre><p>which are already:</p><ul><li>sorted by starting point</li><li>non-overlapping</li></ul><p>and we have:</p><pre>newInterval</pre><p>We need to insert it while maintaining the properties.</p><h3>Example</h3><pre>intervals =<br>[1,3]<br>[6,9]</pre><pre>newInterval =<br>[2,5]</pre><p>We need to insert [2,5].</p><p>But:</p><pre>[1,3]<br>[2,5]</pre><p>overlap.</p><p>So we merge them:</p><pre>[1,5]</pre><p>Final answer:</p><pre>[1,5]<br>[6,9]</pre><h3>🧠 The Three Parts of Insert Interval</h3><p>This problem becomes much easier when we divide it into <strong>three phases</strong>.</p><h3>Phase 1 — Intervals Completely Before New Interval</h3><p>Suppose:</p><pre>[1,2]<br>[3,5]<br>        [6,8]</pre><p>and:</p><pre>newInterval = [6,8]</pre><p>If:</p><pre>intervals[i][1] &lt; newInterval[0]</pre><p>then the current interval is completely before the new interval.</p><p>So simply add it.</p><pre>result.push_back(intervals[i]);</pre><h3>Phase 2 — Overlapping Intervals</h3><p>Now we find intervals that overlap with newInterval.</p><p>The important condition is:</p><pre>intervals[i][0] &lt;= newInterval[1]</pre><p>If this is true, the intervals overlap.</p><p>Then merge them:</p><pre>newInterval[0] =<br>    min(newInterval[0], intervals[i][0]);</pre><pre>newInterval[1] =<br>    max(newInterval[1], intervals[i][1]);</pre><h3>Example</h3><p>Suppose:</p><pre>newInterval = [2,5]</pre><pre>current = [3,7]</pre><p>They overlap because:</p><pre>3 &lt;= 5</pre><p>Merge:</p><pre>start = min(2,3) = 2<br>end = max(5,7) = 7</pre><p>So:</p><pre>[2,5] + [3,7]</pre><p>becomes:</p><pre>[2,7]</pre><h3>Phase 3 — Intervals Completely After New Interval</h3><p>Once we find:</p><pre>current.start &gt; newInterval.end</pre><p>there is no possibility of further overlap.</p><p>So we first insert the merged newInterval:</p><pre>result.push_back(newInterval);</pre><p>Then add all remaining intervals.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tW3cgnU9MC0H4mzep-Mqvg.png" /></figure><h3>🧠 Dry Run</h3><p>Input:</p><pre>intervals =<br>[1,3]<br>[6,9]</pre><pre>newInterval = [2,5]</pre><h3>Phase 1</h3><p>Check:</p><pre>[1,3]</pre><p>Is:</p><pre>3 &lt; 2</pre><p>No.</p><p>So we don’t add it.</p><h3>Phase 2</h3><p>Check:</p><pre>[1,3]</pre><p>Does:</p><pre>1 &lt;= 5</pre><p>Yes.</p><p>So merge:</p><pre>[2,5]<br>[1,3]</pre><p>becomes:</p><pre>[1,5]</pre><p>Next:</p><pre>[6,9]</pre><p>Check:</p><pre>6 &lt;= 5</pre><p>No.</p><p>So overlapping part is finished.</p><h3>Phase 3</h3><p>Add:</p><pre>[1,5]</pre><p>Then add remaining:</p><pre>[6,9]</pre><p>Final:</p><pre>[1,5]<br>[6,9]</pre><h3>LC 452</h3><p>We are trying to <strong>minimize arrows</strong>.</p><p>So we maintain the point where the current arrow is placed.</p><pre>Sort by end<br>     ↓<br>Keep arrow at earliest possible end<br>     ↓<br>If next interval starts after arrow<br>     ↓<br>Need another arrow</pre><h3>LC 57</h3><p>We are trying to <strong>insert one interval correctly</strong>.</p><p>So we divide the intervals into:</p><pre>Before<br>  ↓<br>Overlapping<br>  ↓<br>After</pre><p>This makes the problem much easier to reason about.</p><h3>🧩 My Main Learning From These Problems</h3><p>The biggest thing I learned was that <strong>sorting is not just a preprocessing step</strong>.</p><p>The sorting order determines the greedy strategy.</p><p>For interval problems, I now ask:</p><h3>1️⃣ What does the interval represent?</h3><pre>[start, end]</pre><h3>2️⃣ What should I sort by?</h3><p>Possibilities:</p><pre>start<br>end</pre><h3>3️⃣ What does overlap mean?</h3><p>Usually:</p><pre>start &lt;= currentEnd</pre><h3>4️⃣ What boundary should I maintain?</h3><p>For example:</p><pre>currentEnd<br>arrowPosition<br>newInterval</pre><h3>5️⃣ What greedy decision gives me the most flexibility?</h3><p>For scheduling problems, an <strong>early ending interval</strong> often leaves more room for future intervals.</p><h3>🚀 My Progress</h3><p>Another pattern added to my Greedy toolkit:</p><pre>Greedy<br>   │<br>   ├── Sorting-based Greedy<br>   │<br>   ├── Interval Scheduling ✅<br>   │<br>   ├── Overlapping Intervals ✅<br>   │<br>   └── More patterns to explore...</pre><p>These problems made me realize that <strong>2D arrays and intervals aren’t difficult once the right sorting and boundary logic is clear.</strong></p><p>I’m continuing to build my Greedy problem-solving skills <strong>pattern by pattern</strong>, rather than solving problems randomly.</p><p><strong><em>👋 Connect with Me:<br>🔎LEETCODE :- </em></strong><a href="https://leetcode.com/u/HARSHJAISWAL2707/"><strong><em>https://leetcode.com/u/HARSHJAISWAL2707/</em></strong></a></p><p><strong><em>👤LinkedIn :- </em></strong><a href="https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/"><strong><em>https://www.linkedin.com/in/mr-harsh-jaiswal-76691a32a/</em></strong></a></p><p><strong><em>Written by Mr. Harsh Jaiswal — B.Tech CSE | DSA &amp; Web Development</em></strong></p><p><strong>#IntervalScheduling #OverlappingIntervals #GreedyAlgorithm #DSA #CodingInterview #EnjoyProblems</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5218d2a6417a" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>