<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Alex Mateo</title>
    <description>The latest articles on Forem by Alex Mateo (@expora).</description>
    <link>https://forem.com/expora</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3890393%2F271643fa-4171-46e0-9272-0a884bd474a5.png</url>
      <title>Forem: Alex Mateo</title>
      <link>https://forem.com/expora</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/expora"/>
    <language>en</language>
    <item>
      <title>Sliding Window &amp; Two Pointers: The Decision Framework Nobody Teaches You</title>
      <dc:creator>Alex Mateo</dc:creator>
      <pubDate>Thu, 14 May 2026 09:53:58 +0000</pubDate>
      <link>https://forem.com/expora/sliding-window-two-pointers-the-decision-framework-nobody-teaches-you-4p4c</link>
      <guid>https://forem.com/expora/sliding-window-two-pointers-the-decision-framework-nobody-teaches-you-4p4c</guid>
      <description>&lt;p&gt;Most people learn sliding window and two pointers as two separate techniques, practice them in isolation, and then freeze when a new array problem appears because they can't tell which one applies.&lt;/p&gt;

&lt;p&gt;The problem isn't that the techniques are hard. The problem is that nobody teaches the &lt;em&gt;decision&lt;/em&gt; — the moment before you write code where you look at the problem and know, without guessing, which pattern fits.&lt;/p&gt;

&lt;p&gt;This guide fixes that.&lt;/p&gt;




&lt;h2&gt;
  
  
  What sliding window actually solves
&lt;/h2&gt;

&lt;p&gt;Sliding window is for problems where you need to find or optimize a &lt;strong&gt;contiguous subarray or substring&lt;/strong&gt; that satisfies some condition. The word contiguous is the key. You're not picking elements from anywhere in the array — you need a connected segment.&lt;/p&gt;

&lt;p&gt;The window metaphor is accurate: you have a left and right boundary, and you move them to expand or shrink a range while tracking something inside it (a sum, a character count, a frequency map).&lt;/p&gt;

&lt;p&gt;The reason it works efficiently is that instead of recalculating from scratch every time the window changes, you update incrementally. Remove the leftmost element, add the new rightmost element, update your state. Most operations become O(1) per step, making the overall complexity O(n) instead of O(n²).&lt;/p&gt;

&lt;p&gt;There are two variants and confusing them leads to buggy implementations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixed-size window&lt;/strong&gt; — the window size is given in the problem. Maximum sum subarray of size k. Average of every subarray of length k. Here you advance both pointers in lockstep, maintaining a constant distance between them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variable-size window&lt;/strong&gt; — the window grows and shrinks based on a condition. Longest substring without repeating characters. Minimum window substring containing all target characters. Here the right pointer advances to expand, and the left pointer catches up when the condition is violated.&lt;/p&gt;




&lt;h2&gt;
  
  
  What two pointers actually solves
&lt;/h2&gt;

&lt;p&gt;Two pointers is for problems where you need to find a pair, a triplet, or a partition based on a mathematical relationship — not a subarray, but a &lt;em&gt;combination&lt;/em&gt; of elements.&lt;/p&gt;

&lt;p&gt;The classic setup: a sorted array, find two numbers that sum to a target. You start with one pointer at the beginning and one at the end. If the sum is too small, move the left pointer right. If it's too large, move the right pointer left. You're using the sorted order to eliminate half the search space with each step.&lt;/p&gt;

&lt;p&gt;This is fundamentally different from sliding window. You're not maintaining a window — you're navigating a relationship between two positions that can move independently, in opposite directions or the same direction, depending on the problem.&lt;/p&gt;

&lt;p&gt;The non-obvious insight about two pointers is that it often applies to problems that don't immediately look like pair-finding. Container with most water. Trapping rain water. Removing duplicates in place. These look different on the surface but all follow the same logic: two positions scanning inward (or outward) based on a condition, using the array's structure to prune the search space.&lt;/p&gt;




&lt;h2&gt;
  
  
  The decision framework
&lt;/h2&gt;

&lt;p&gt;When you see an array or string problem, two questions narrow the pattern almost instantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First question: are you looking for a contiguous subarray, or a combination of elements?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the answer is contiguous — a substring, a subarray, a window — you're in sliding window territory. If the answer is a pair, triplet, partition, or two elements with a relationship, you're looking at two pointers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second question: does the array need to be sorted (or can it be sorted without losing information)?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Two pointers on an unsorted array usually doesn't work because you lose the ability to decide which pointer to move. If sorting is required and valid, two pointers is likely the right tool. If the problem specifies the order matters and you can't sort, you're probably in sliding window territory.&lt;/p&gt;

&lt;p&gt;This table makes the decision explicit:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal in the problem&lt;/th&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;"Subarray", "substring", "contiguous"&lt;/td&gt;
&lt;td&gt;Sliding window&lt;/td&gt;
&lt;td&gt;Need to track a connected range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Sum equals target", sorted input&lt;/td&gt;
&lt;td&gt;Two pointers&lt;/td&gt;
&lt;td&gt;Sort + inward scan eliminates search space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Longest/shortest subarray with condition"&lt;/td&gt;
&lt;td&gt;Sliding window (variable)&lt;/td&gt;
&lt;td&gt;Shrink/grow window based on constraint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Pair that satisfies X", sorted or sortable&lt;/td&gt;
&lt;td&gt;Two pointers&lt;/td&gt;
&lt;td&gt;Independent pointer movement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Remove duplicates in place"&lt;/td&gt;
&lt;td&gt;Two pointers&lt;/td&gt;
&lt;td&gt;Read/write pointer separation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fixed subarray size, rolling computation&lt;/td&gt;
&lt;td&gt;Sliding window (fixed)&lt;/td&gt;
&lt;td&gt;Incremental update O(1) per step&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The single question that separates the two most clearly: &lt;em&gt;"Do I need these elements to be adjacent in the original array?"&lt;/em&gt; If yes, sliding window. If no, two pointers.&lt;/p&gt;




&lt;h2&gt;
  
  
  The templates
&lt;/h2&gt;

&lt;p&gt;Both patterns have clean templates that you should know cold. Not memorized — &lt;em&gt;understood&lt;/em&gt;, so you can reconstruct them under pressure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixed-size sliding window:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fixed_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;window_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;window_sum&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;window_sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key line is &lt;code&gt;arr[i] - arr[i - k]&lt;/code&gt;: you add the incoming element and subtract the one that just left. No recomputation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variable-size sliding window:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;variable_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;   &lt;span class="c1"&gt;# or a counter, sum, set — depends on the problem
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="c1"&gt;# expand: include arr[right] in state
&lt;/span&gt;        &lt;span class="c1"&gt;# ...
&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;condition_violated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# shrink: remove arr[left] from state
&lt;/span&gt;            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The while loop is the core. The right pointer always moves forward. The left pointer only moves when the current window violates the constraint. The window size at any moment is &lt;code&gt;right - left + 1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two pointers (sorted array, sum target):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;two_sum_sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The invariant: at every step, you're narrowing the search space by one. &lt;code&gt;left &amp;lt; right&lt;/code&gt; ensures you never check the same pair twice.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problems that make these patterns click
&lt;/h2&gt;

&lt;p&gt;There are specific problems that reveal the pattern clearly rather than hiding it behind complexity. These are the ones worth doing before jumping to harder variations.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;fixed-size sliding window&lt;/strong&gt;, start with Maximum Average Subarray I (LeetCode 643) and Maximum Sum of Subarray of Size K. These problems make the incremental update concrete.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;variable-size sliding window&lt;/strong&gt;, Longest Substring Without Repeating Characters (LeetCode 3) is the canonical example. The character frequency map and the shrinking left pointer are the template in its simplest form. After this, Minimum Window Substring (LeetCode 76) is the harder version that tests whether you understood the shrinking logic or just memorized it.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;two pointers&lt;/strong&gt;, start with Two Sum II (LeetCode 167) — the sorted version. Then Container With Most Water (LeetCode 11), which uses the same inward-scanning logic but applied to a geometry problem. 3Sum (LeetCode 15) extends two pointers by adding an outer loop, and it's the most common two pointers problem in FAANG interviews.&lt;/p&gt;

&lt;p&gt;The thing that connects all of these: solve them by tracing through a small example by hand first. Draw the array. Draw the pointers. Move them step by step. The visual trace makes the logic stable in a way that reading code never does.&lt;/p&gt;




&lt;h2&gt;
  
  
  What interviewers actually test
&lt;/h2&gt;

&lt;p&gt;In a sliding window or two pointers interview, the algorithm itself is table stakes. What separates good candidates is everything around it.&lt;/p&gt;

&lt;p&gt;Strong candidates articulate the contiguous vs. combination distinction before writing code. They say "I'm using sliding window because I need a subarray — the elements need to be adjacent" and that sentence shows the interviewer you're not pattern-matching on keywords.&lt;/p&gt;

&lt;p&gt;They also handle the edge cases clearly: empty input, window larger than array, no valid window exists. These cases are easy to handle once the main logic is clean — but candidates who don't understand the pattern deeply always forget them.&lt;/p&gt;

&lt;p&gt;The bug that appears most often in sliding window implementations is an off-by-one in the window size calculation. &lt;code&gt;right - left + 1&lt;/code&gt; is the window size, not &lt;code&gt;right - left&lt;/code&gt;. Drawing the window on paper before coding eliminates this.&lt;/p&gt;

&lt;p&gt;The bug that appears most often in two pointers is updating pointers incorrectly when the target is found — advancing only one pointer instead of both, which causes an infinite loop. The fix: when you find a match, do &lt;code&gt;left += 1&lt;/code&gt; and &lt;code&gt;right -= 1&lt;/code&gt; simultaneously.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to study so these patterns transfer
&lt;/h2&gt;

&lt;p&gt;The mistake most people make is solving twenty sliding window problems in isolation and then interleaving random LeetCode problems expecting the pattern to stick. It doesn't work that way.&lt;/p&gt;

&lt;p&gt;What works is blocked practice followed by interleaving. Solve ten sliding window problems in a row — fixed window, then variable window, then mixed. Then solve ten two pointers problems. Then, and only then, mix them. The goal of the blocked phase is to build recognition. The goal of the interleaving phase is to test whether the recognition is real or superficial.&lt;/p&gt;

&lt;p&gt;After each problem, write one sentence before closing the tab: &lt;em&gt;"This was sliding window because ___"&lt;/em&gt; or &lt;em&gt;"This was two pointers because ___"&lt;/em&gt;. That sentence forces you to articulate the why, which is exactly what you'll need to do in the interview.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between sliding window and two pointers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sliding window is for contiguous subarrays or substrings where you maintain a connected range with left and right boundaries. Two pointers is for pair or partition problems where two positions scan the array independently, often from opposite ends. The key question: do the elements need to be adjacent?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When should I use sliding window vs two pointers in an interview?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use sliding window when the problem asks for a subarray, substring, or contiguous section satisfying a condition. Use two pointers when the problem involves finding pairs, triplets, or requires sorting the input and scanning inward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the time complexity of sliding window?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O(n) for both fixed and variable window. Each element is added to and removed from the window at most once, making the total operations linear regardless of window size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is two pointers O(n)?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes for the standard sorted-array variant. Each pointer moves at most n steps in total, giving O(n) after the initial sort (which is O(n log n), making the overall complexity O(n log n) when sorting is required).&lt;/p&gt;




&lt;p&gt;I built &lt;a href="https://tryexpora.com" rel="noopener noreferrer"&gt;Expora&lt;/a&gt; to make exactly this kind of pattern recognition visual. The sliding window debugger shows the left and right boundaries moving in real time, the window contents updating with each step, and the state (sum, frequency map, etc.) changing incrementally. Seeing it run on your own code makes the template feel obvious rather than arbitrary.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://tryexpora.com/blog/sliding-window-two-pointers" rel="noopener noreferrer"&gt;tryexpora.com/blog/sliding-window-two-pointers&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>programming</category>
      <category>career</category>
      <category>leetcode</category>
    </item>
    <item>
      <title>Graph Algorithms for Coding Interviews: When to Use BFS, DFS, or Dijkstra</title>
      <dc:creator>Alex Mateo</dc:creator>
      <pubDate>Thu, 23 Apr 2026 08:12:38 +0000</pubDate>
      <link>https://forem.com/expora/graph-algorithms-for-coding-interviews-when-to-use-bfs-dfs-or-dijkstra-5b75</link>
      <guid>https://forem.com/expora/graph-algorithms-for-coding-interviews-when-to-use-bfs-dfs-or-dijkstra-5b75</guid>
      <description>&lt;p&gt;Graphs are the most feared topic in coding interviews — not because they're impossible, but because most people learn BFS, DFS, and Dijkstra in isolation without understanding &lt;em&gt;when&lt;/em&gt; to use each one.&lt;/p&gt;

&lt;p&gt;This guide gives you the decision framework that turns graph problems from guesswork into a systematic process.&lt;/p&gt;




&lt;h2&gt;
  
  
  The three algorithms every interviewer expects you to know
&lt;/h2&gt;

&lt;h3&gt;
  
  
  BFS — Breadth-First Search
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Data structure:&lt;/strong&gt; Queue (FIFO)&lt;br&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; O(V + E) time, O(V) space&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need the shortest path in an unweighted graph&lt;/li&gt;
&lt;li&gt;You need the minimum number of steps to reach a target&lt;/li&gt;
&lt;li&gt;You need to explore nodes level by level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Don't use when:&lt;/strong&gt; the graph has weighted edges — BFS treats all edges as equal cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classic problems:&lt;/strong&gt; Binary Tree Level Order Traversal, Rotting Oranges, Word Ladder, Shortest Path in Binary Matrix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; Always say explicitly: &lt;em&gt;"I'm using BFS because I need the shortest path in terms of edges, and BFS guarantees the first time I reach a node it's via the shortest path."&lt;/em&gt; That sentence shows you understand the why, not just the how.&lt;/p&gt;




&lt;h3&gt;
  
  
  DFS — Depth-First Search
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Data structure:&lt;/strong&gt; Stack (implicit via recursion, or explicit)&lt;br&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; O(V + E) time, O(V) space&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to detect cycles&lt;/li&gt;
&lt;li&gt;You need to find all paths (not just shortest)&lt;/li&gt;
&lt;li&gt;You need to check connectivity or count connected components&lt;/li&gt;
&lt;li&gt;You need topological sort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Don't use when:&lt;/strong&gt; you need the shortest path — DFS doesn't guarantee it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classic problems:&lt;/strong&gt; Number of Islands, Course Schedule, Clone Graph, Pacific Atlantic Water Flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; The most common DFS bug is forgetting to mark nodes as visited &lt;em&gt;before&lt;/em&gt; recursing. Always set &lt;code&gt;visited[node] = true&lt;/code&gt; at the start, before processing neighbors.&lt;/p&gt;




&lt;h3&gt;
  
  
  Dijkstra's Algorithm
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Data structure:&lt;/strong&gt; Min-heap (priority queue)&lt;br&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; O((V + E) log V) time, O(V) space&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The graph has weighted edges with non-negative weights&lt;/li&gt;
&lt;li&gt;You need the shortest distance from source to target (or all nodes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Don't use when:&lt;/strong&gt; the graph has negative edge weights — use Bellman-Ford instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Classic problems:&lt;/strong&gt; Network Delay Time, Path With Minimum Effort, Cheapest Flights Within K Stops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interview tip:&lt;/strong&gt; The Dijkstra pattern is always the same: push &lt;code&gt;(cost, node)&lt;/code&gt; to the heap → pop cheapest → skip if visited → process neighbors. Know O((V+E) log V) and be able to explain why.&lt;/p&gt;




&lt;h2&gt;
  
  
  The decision framework
&lt;/h2&gt;

&lt;p&gt;When you see a graph problem, run through this before writing a single line of code:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unweighted graph, need shortest path&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;td&gt;Expands level by level — first reach = shortest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum number of steps / hops&lt;/td&gt;
&lt;td&gt;BFS&lt;/td&gt;
&lt;td&gt;"Steps" = edges = unweighted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weighted graph, all weights ≥ 0&lt;/td&gt;
&lt;td&gt;Dijkstra&lt;/td&gt;
&lt;td&gt;Greedy expansion via min-heap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weighted graph with negative edges&lt;/td&gt;
&lt;td&gt;Bellman-Ford&lt;/td&gt;
&lt;td&gt;Dijkstra's greedy assumption breaks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cycle detection&lt;/td&gt;
&lt;td&gt;DFS&lt;/td&gt;
&lt;td&gt;Track visited + in-stack state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All paths, not just shortest&lt;/td&gt;
&lt;td&gt;DFS + backtracking&lt;/td&gt;
&lt;td&gt;BFS doesn't enumerate all paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connected components / flood fill&lt;/td&gt;
&lt;td&gt;DFS or BFS&lt;/td&gt;
&lt;td&gt;Either works — DFS simpler recursively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topological sort&lt;/td&gt;
&lt;td&gt;DFS or Kahn's BFS&lt;/td&gt;
&lt;td&gt;DFS post-order = reverse topological order&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The single question to ask first:&lt;/strong&gt; &lt;em&gt;"Does the graph have weights?"&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No → choose between BFS (shortest path) and DFS (full exploration)&lt;/li&gt;
&lt;li&gt;Yes → Dijkstra (non-negative) or Bellman-Ford (negative weights)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That question narrows the decision from three options to two in almost every case.&lt;/p&gt;




&lt;h2&gt;
  
  
  What interviewers actually look for
&lt;/h2&gt;

&lt;p&gt;Senior interviewers rarely test whether you can implement BFS from scratch. They test whether you can &lt;em&gt;reason&lt;/em&gt; about graph problems systematically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What separates strong candidates:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clarifying the graph structure before coding.&lt;/strong&gt; Directed or undirected? Weighted or unweighted? Can there be cycles? These change the algorithm — asking shows you're not pattern-matching blindly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Explaining why you chose the algorithm.&lt;/strong&gt; "I'm using BFS because the graph is unweighted and I need minimum steps" is a complete answer. "I'll use BFS" is not.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Recognizing implicit graphs.&lt;/strong&gt; Many problems don't say "graph" — they present a grid, a word transformation, a dependency list. Rotting Oranges is multi-source BFS. Word Ladder is shortest path on an implicit graph.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Knowing complexity without being asked.&lt;/strong&gt; O(V + E) for BFS and DFS, O((V + E) log V) for Dijkstra. Being able to &lt;em&gt;derive&lt;/em&gt; these (not just recite them) is the difference at top companies.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  How to study so graph algorithms actually stick
&lt;/h2&gt;

&lt;p&gt;Most resources explain algorithms in isolation — here's how BFS works, here's DFS — without building the mental model of &lt;em&gt;when&lt;/em&gt; to use each one.&lt;/p&gt;

&lt;p&gt;What actually works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Watch execution before writing code.&lt;/strong&gt; For BFS, watch the queue grow and shrink level by level. For Dijkstra, watch the min-heap pop nodes in cost order and the distance table update. This visual step makes implementation feel natural.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Implement on the same graph.&lt;/strong&gt; Take a 5-node, 7-edge graph. Run BFS, then DFS, then Dijkstra (with weights). Seeing three different traversal orders on the same graph makes the differences concrete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Practice on grid problems.&lt;/strong&gt; A 2D grid is a graph where each cell is a node and adjacent cells are edges. Grid problems are the most common disguised graph problems in interviews — the pattern transfers directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Build the decision reflex.&lt;/strong&gt; After solving, write one sentence: &lt;em&gt;"This is BFS because the graph is unweighted and I need shortest path."&lt;/em&gt; Train the recognition, not just the implementation.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When should I use BFS vs DFS in a coding interview?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use BFS when you need the shortest path in an unweighted graph or the minimum number of steps. Use DFS when you need all paths, cycle detection, or connectivity. If shortest path isn't required, both work — DFS is simpler to implement recursively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between BFS and Dijkstra?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;BFS finds shortest path by number of edges (unweighted, every edge = cost 1). Dijkstra finds shortest path by total weight (weighted, uses min-heap). BFS for "minimum hops", Dijkstra for "minimum cost."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can DFS find the shortest path?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. DFS finds &lt;em&gt;a&lt;/em&gt; path, not necessarily the shortest. Use BFS for unweighted shortest path, Dijkstra for weighted.&lt;/p&gt;




&lt;p&gt;I built &lt;a href="https://tryexpora.com" rel="noopener noreferrer"&gt;Expora&lt;/a&gt; to solve exactly this — the gap between knowing how an algorithm works and knowing when to use it. Expora's visual debuggers show BFS, DFS, and Dijkstra executing step by step on real graphs, so you build the decision intuition that interviewers test, not just the ability to implement from memory.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://tryexpora.com/blog/graph-algorithms-coding-interview" rel="noopener noreferrer"&gt;tryexpora.com/blog/graph-algorithms-coding-interview&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>programming</category>
      <category>career</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why LeetCode is So Hard — and Why Grinding More Problems Won't Help</title>
      <dc:creator>Alex Mateo</dc:creator>
      <pubDate>Tue, 21 Apr 2026 08:23:18 +0000</pubDate>
      <link>https://forem.com/expora/why-leetcode-is-so-hard-and-why-grinding-more-problems-wont-help-18oc</link>
      <guid>https://forem.com/expora/why-leetcode-is-so-hard-and-why-grinding-more-problems-wont-help-18oc</guid>
      <description>&lt;p&gt;Here's why grinding leads to memorization, not understanding, and what to do instead.&lt;/p&gt;

&lt;p&gt;You've solved 150 problems. You read the editorial. You watched the NeetCode video.&lt;/p&gt;

&lt;p&gt;And then a slightly different problem appears in the interview and you blank.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's not you. It's what you've been practicing.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The real reason LeetCode feels impossible
&lt;/h2&gt;

&lt;p&gt;When developers say LeetCode is hard, they usually mean the problems are tricky or&lt;br&gt;
they can't figure out the right approach. But there's a third reason nobody talks&lt;br&gt;
about — &lt;strong&gt;LeetCode only shows you the answer, not the thinking&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Submit a solution. Green checkmark. Move on.&lt;/p&gt;

&lt;p&gt;What the platform doesn't show you is what happened &lt;em&gt;inside&lt;/em&gt; the algorithm: which&lt;br&gt;
variable changed, why a pointer moved, what the data structure looked like at step 3.&lt;br&gt;
You only ever see the output.&lt;/p&gt;

&lt;p&gt;This matters because coding interviews don't test if you know the answer.&lt;br&gt;
They test if you can &lt;strong&gt;explain the reasoning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When an interviewer asks "why did you use a hashmap here?", the correct answer&lt;br&gt;
isn't the solution — it's the thought process behind it. LeetCode trains you to&lt;br&gt;
produce solutions. It doesn't train you to understand them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What LeetCode shows you&lt;/th&gt;
&lt;th&gt;What you actually need&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✗ Input and output&lt;/td&gt;
&lt;td&gt;✓ What the algorithm does at each step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✗ Runtime and memory stats&lt;/td&gt;
&lt;td&gt;✓ Why a specific data structure was chosen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✗ Green or red checkmark&lt;/td&gt;
&lt;td&gt;✓ How the state changes with each decision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✗ The accepted solution (after you fail)&lt;/td&gt;
&lt;td&gt;✓ The intuition that transfers to new problems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That gap between "submitting a solution" and "understanding an algorithm" is why&lt;br&gt;
LeetCode feels hard even after hundreds of problems.&lt;/p&gt;


&lt;h2&gt;
  
  
  What grinding actually does to your brain
&lt;/h2&gt;

&lt;p&gt;The conventional advice is: do more problems. Grind 75, then 150, then 300.&lt;br&gt;
The theory is that pattern recognition emerges from volume.&lt;/p&gt;

&lt;p&gt;Here's the problem: &lt;strong&gt;pattern recognition requires understanding, not repetition&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can't recognize that a problem needs a sliding window if you only know that a&lt;br&gt;
previous problem that &lt;em&gt;looked similar&lt;/em&gt; used a sliding window. That's memorization.&lt;/p&gt;

&lt;p&gt;It breaks the moment the problem is phrased differently.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The grinding trap:&lt;/strong&gt; You solve problem #47 and feel like you understand&lt;br&gt;
two pointers. Three weeks later, a different two-pointer problem appears and&lt;br&gt;
you can't start it. You go back and re-solve problem #47. The cycle repeats.&lt;br&gt;
You're not building understanding — you're fighting the Ebbinghaus forgetting&lt;br&gt;
curve with repetition instead of comprehension.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The developers who pass FAANG interviews typically haven't solved &lt;em&gt;more&lt;/em&gt; problems&lt;br&gt;
than the ones who fail. They've understood &lt;em&gt;fewer&lt;/em&gt; problems more deeply.&lt;/p&gt;


&lt;h2&gt;
  
  
  The difference between memorizing and understanding
&lt;/h2&gt;

&lt;p&gt;Here's a concrete example. Most developers who have done LeetCode know Two Sum.&lt;/p&gt;

&lt;p&gt;Ask them to solve it: they'll write a hashmap solution in two minutes.&lt;/p&gt;

&lt;p&gt;Now ask them: "If the input array is &lt;strong&gt;sorted&lt;/strong&gt;, which approach is better?"&lt;/p&gt;

&lt;p&gt;Many will still reach for the hashmap — because that's what they practiced.&lt;br&gt;
They memorized the solution, not the reasoning behind choosing it.&lt;/p&gt;

&lt;p&gt;Understanding means being able to trace through the algorithm at each step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nums = [2, 7, 11, 15], target = 9

Step 1: L=0, R=3 → sum = 2+15 = 17 &amp;gt; 9
        → Too large. Move R left.
        → WHY: sorted array, moving R left always decreases sum.

Step 2: L=0, R=2 → sum = 2+11 = 13 &amp;gt; 9
        → Still too large. Move R left again.

Step 3: L=0, R=1 → sum = 2+7 = 9 ✓
        → Found it.

Key insight: sorted input makes two pointers strictly better than hashmap.
O(n) time, O(1) space vs O(n) space. The code is almost identical —
the understanding is completely different.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference isn't the code. It's being able to explain &lt;em&gt;why&lt;/em&gt; the approach&lt;br&gt;
changes when the input is sorted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding an algorithm means answering 3 questions at any step
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What is the current state?&lt;/strong&gt; Where are the pointers, what's in the queue,&lt;br&gt;
what does the data structure look like right now?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why did the last step happen?&lt;/strong&gt; What condition triggered this move,&lt;br&gt;
this push, this comparison?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What will happen next?&lt;/strong&gt; Given the current state, can you predict the&lt;br&gt;
algorithm's next decision &lt;em&gt;before it happens&lt;/em&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can't answer any of these by reading the accepted solution. You need to&lt;br&gt;
&lt;em&gt;see&lt;/em&gt; the algorithm run.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to do instead of grinding
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Learn patterns, not solutions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most coding interview problems are variations of 7-10 core patterns: Sliding Window,&lt;br&gt;
Two Pointers, BFS, DFS, Binary Search, DP, and Dijkstra. Learn them at the&lt;br&gt;
pattern level — not the problem level. When you recognize the pattern, the&lt;br&gt;
solution follows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. See execution, not just code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before writing anything, trace through the algorithm manually. Watch the state&lt;br&gt;
change. Understand why each step happens. Only then write the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Solve fewer problems, more deeply&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One deeply understood problem — where you can explain every step, every decision,&lt;br&gt;
every tradeoff — is worth more than 10 memorized solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Practice explaining out loud&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After solving, close the editor and explain the solution as if talking to an&lt;br&gt;
interviewer. If you can't explain why each line exists, you memorized it.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is LeetCode worth it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;LeetCode is worth it if you use it to build understanding, not just to accumulate&lt;br&gt;
solved problems. Used correctly — slowly, with deliberate tracing — it's a useful&lt;br&gt;
problem bank. Used as a grinding machine, it builds recall that breaks under&lt;br&gt;
interview pressure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How many LeetCode problems do I need for Google or Meta?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's no magic number. Being able to trace 50 algorithms step by step and explain&lt;br&gt;
every decision beats having memorized 300 solutions. Interviewers evaluate your&lt;br&gt;
reasoning, not your problem count.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is grinding LeetCode?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Grinding LeetCode means solving a high volume of problems (150-300+) with the goal&lt;br&gt;
that pattern recognition emerges from volume. It's the dominant prep approach, but&lt;br&gt;
it fails when problems are phrased differently or when interviewers ask you to&lt;br&gt;
explain your reasoning.&lt;/p&gt;




&lt;p&gt;I built &lt;a href="https://tryexpora.com" rel="noopener noreferrer"&gt;Expora&lt;/a&gt; specifically because I was that developer —&lt;br&gt;
I had Grokking Algorithms, the algorithm bible, three courses, and 200 LeetCode&lt;br&gt;
problems solved. I still froze in interviews because I was memorizing, not&lt;br&gt;
understanding.&lt;/p&gt;

&lt;p&gt;Expora's visual debuggers let you step through algorithms one operation at a time —&lt;br&gt;
watching the graph update, the queue fill, the dp table populate — with your own&lt;br&gt;
code. It's the difference between reading about an algorithm and watching it think.&lt;/p&gt;

&lt;p&gt;If you've ever felt like grinding isn't working, you're probably right.&lt;br&gt;
The problem isn't your intelligence — it's the feedback loop you're practicing in.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://tryexpora.com/blog/why-leetcode-is-hard" rel="noopener noreferrer"&gt;tryexpora.com/blog/why-leetcode-is-hard&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>programming</category>
      <category>career</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
