Skip to content
← Journal
12 min readAta Mohammadi

The Frontend Engineer's LeetCode Blueprint, Part 1: The Linear Gauntlet

Frontend habits like map/filter/reduce chains fail LeetCode's hidden test suites. Part 1 covers V8 array internals, two pointers and sliding windows, with worked solutions for 3Sum, Minimum Window Substring and Trapping Rain Water.

Part 1: The Linear Gauntlet (Two Pointers, Sliding Windows, and V8 Realities)


The Confession

Let’s be honest with each other.

You build complex state machines in React. You configure webpack plugins in your sleep. You’ve debugged race conditions inside nested custom hooks and wrestled with WebGL pipelines. You are a senior frontend engineer.

Then you sit down for a live coding screen—or you open LeetCode to prepare for a Toptal round—and you get handed this:

"Given an array of integers height, find two lines that together with the x-axis form a container, such that the container contains the most water."

Your brain immediately fires the frontend reflex:

const maxWater = heights.map((h, i) => ...).filter(...).reduce(...)

Ten minutes later, you have three nested loops disguised as chained array prototypes, your browser tab freezes, and the console spits out: Time Limit Exceeded (TLE).

Here is the truth: Frontend programming rewards declarative, immutable, defensive abstractions. LeetCode and elite technical screenings (like Toptal's Codility gauntlet) reward imperative, mechanical sympathy, pointer manipulation, and memory layout awareness.

In this 5-part series, we are going to deconstruct algorithms through the lens of modern JavaScript. We are starting from ground zero and climbing to the top tier: solving Hard-rated problems with predictable, production-grade JavaScript that will blast through automated screening test suites.

Welcome to Part 1: The Linear Gauntlet.


1. JavaScript Engine Realities: What LeetCode Won't Tell You

Before writing a single pointer, we have to talk about how V8 (Node.js/Chrome) executes JavaScript. In LeetCode Medium/Hard challenges, an algorithm with the right theoretical Big-O can still fail due to hidden constant-factor overhead in JavaScript.

Hidden Trap #1: Array.prototype.shift() and splice()

In frontend apps, array.shift() is harmless for a dropdown list with 10 items. In LeetCode:

  • array.pop() runs in O(1) amortized time. It trims the tail.
  • array.shift() runs in O(n) time. It forces the engine to re-index every single element in memory.

If you write a loop of n elements and call shift() inside it, your O(n) algorithm just silently mutated into an O(n^2) slog.

Hidden Trap #2: V8 Array Representation (Fast Elements vs. Dictionary Mode)

In V8, JavaScript arrays aren't traditional C-style contiguous memory blocks by default, but V8 tries to make them so if you keep them homogeneous:

  • PACKED_SMI_ELEMENTS: Contiguous memory of 31-bit integers. Fastest.
  • HOLEY_ELEMENTS: If you skip an index (arr[100] = 5 when arr.length was 2), V8 drops optimizations and may convert the array into a hash map lookup internally.
// DO NOT do this in interview problems:
const arr = [];
arr[100000] = 1; // De-optimized into a slow sparse array (Dictionary Mode)

// DO this:
const arr = new Array(100000).fill(0); // Dense, contiguous block

Hidden Trap #3: Map vs. Plain Object {}

  • Plain Object keys are converted to strings or symbols. Number keys become strings (obj[1] accesses key "1").
  • In high-throughput lookups (10^5 operations), new Map() or a typed array (new Int32Array(n)) avoids unnecessary string serialization and prototype lookups. If your keys are contiguous numbers or ASCII characters, a fixed-size Uint32Array or Array(256).fill(0) will run 10x faster than {}.

2. Pattern 1: Two Pointers (Converging & Traversal)

Two Pointers is the art of eliminating an entire nested loop by exploiting sorted order or symmetrical properties.

The Mental Model

When you see:

  1. A sorted array (or a problem where sorting first takes O(n log n) without violating space limits).
  2. A search for pairs, triplets, or sub-segments that satisfy an inequality or sum.
  3. In-place array transformations with O(1) auxiliary space.

Your default intuition shouldn't be nested loops (O(n^2)); it should be two indices closing in from opposite boundaries (O(n)).

Left ->                                            <- Right
[ -4,   -1,   -1,    0,    1,    2 ]

Challenge 1: 3Sum (LeetCode 15 - Medium / Toptal Classic)

Problem: Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] === 0. The solution set must not contain duplicate triplets.

Why Frontenders Fail This:

Most candidates know to sort the array. But they get crushed by the duplicate triplets requirement. They use JSON.stringify() inside a Set to filter duplicates, which destroys their runtime and balloons memory to O(n^2).

The Mechanical Strategy:

  1. Sort the array numerically: nums.sort((a, b) => a - b). Complexity: O(n log n).
  2. Iterate with pointer i from index 0 to nums.length - 3.
  3. If nums[i] > 0, break early. (Three positive numbers can never sum to zero in a sorted array).
  4. If nums[i] === nums[i - 1], skip it! This eliminates duplicates for the first element.
  5. Use two pointers: left = i + 1, right = nums.length - 1.
  6. Calculate sum = nums[i] + nums[left] + nums[right]:
    • If sum === 0: Push [nums[i], nums[left], nums[right]] to results.
    • Then advance left past any identical elements (while (nums[left] === nums[left + 1]) left++).
    • Advance right past any identical elements (while (nums[right] === nums[right - 1]) right--).
    • Move both pointers inwards: left++, right--.
    • If sum < 0: We need a larger number → left++.
    • If sum > 0: We need a smaller number → right--.

The Code:

/**
 * Time Complexity:  O(n^2) - O(n log n) sort + n iterations of O(n) two-pointer sweep
 * Space Complexity: O(1) auxiliary (ignoring output array & sort stack space)
 * @param {number[]} nums
 * @return {number[][]}
 */
function threeSum(nums) {
  const result = [];
  if (!nums || nums.length < 3) return result;

  // Crucial: JavaScript default sort converts numbers to strings!
  // Always pass the numeric comparator: (a, b) => a - b
  nums.sort((a, b) => a - b);

  const len = nums.length;

  for (let i = 0; i < len - 2; i++) {
    // Optimization: Smallest number > 0 means impossible to sum to 0
    if (nums[i] > 0) break;

    // Skip duplicate values for the first element of the triplet
    if (i > 0 && nums[i] === nums[i - 1]) continue;

    let left = i + 1;
    let right = len - 1;

    while (left < right) {
      const sum = nums[i] + nums[left] + nums[right];

      if (sum === 0) {
        result.push([nums[i], nums[left], nums[right]]);

        // Skip consecutive duplicates for left pointer
        while (left < right && nums[left] === nums[left + 1]) left++;
        // Skip consecutive duplicates for right pointer
        while (left < right && nums[right] === nums[right - 1]) right--;

        // Advance past the processed unique values
        left++;
        right--;
      } else if (sum < 0) {
        // Sum too small, move left pointer rightward to increase value
        left++;
      } else {
        // Sum too large, move right pointer leftward to decrease value
        right--;
      }
    }
  }

  return result;
}

3. Pattern 2: The Sliding Window (Fixed vs. Dynamic)

If Two Pointers is two people walking towards each other, the Sliding Window is an accordion expanding and contracting across an array or string.

When to Use It:

  • Longest/shortest contiguous subarray or substring matching a condition.
  • Fixed-size rolling calculations (e.g., maximum sum of k consecutive elements).
  • Frequency matching (anagrams, permutations).

The Dynamic Sliding Window State Machine:

Every dynamic window follows this universal loop invariant:

  1. Expand: Increment right pointer, incorporate array[right] into your window state.
  2. Evaluate: Check if the window is now "valid" (or "invalid").
  3. Contract: While the condition demands it, increment left pointer, strip array[left] out of your window state, and update the optimal answer.

Challenge 2: Minimum Window Substring (LeetCode 76 - Hard)

This is one of the highest-yield interview problems in history. It tests string iteration, map frequency reconciliation, and window-boundary discipline.

Problem: Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring, return "".

The Architecture of the Solution:

Instead of constantly comparing two hash maps of character counts (which introduces a hidden O(26) or O(128) check on every step), we maintain:

  1. needMap: Target frequencies of characters in t.
  2. required: Number of unique characters in t whose frequencies must be satisfied.
  3. formed: How many unique characters currently meet the frequency requirement in the current window.

Step-by-Step Execution:

  • Move right. If the character is in needMap, record its presence in windowMap.
  • When windowMap[char] === needMap[char], increment formed++.
  • Once formed === required, the window contains all needed characters!
  • Now squeeze from the left:
    • Check if the current window length is the smallest seen so far. If so, record start = left and minLen = right - left + 1.
    • Eject s[left] from windowMap.
    • If windowMap[s[left]] < needMap[s[left]], our window is no longer complete! Decrement formed--.
    • Increment left.
  • Repeat until right reaches the end of string s.
Target: "ABC" -> need: {A:1, B:1, C:1}, required: 3

s = " A  D  O  B  E  C  O  D  E  B  A  N  C "
    [-----------------]                       -> Window has A, B, C! formed == 3
       [--------------]                       -> Shrink 'A': formed drops to 2.
                                      [-----] -> New minimum window: "BANC"

The Code:

/**
 * Time Complexity:  O(|s| + |t|) - each character in s is visited at most twice (left and right)
 * Space Complexity: O(|s| + |t|) - frequency maps for ASCII / UTF-16 characters
 * @param {string} s
 * @param {string} t
 * @return {string}
 */
function minWindow(s, t) {
  if (!s || !t || s.length < t.length) return "";

  // 1. Build frequency profile for t
  const targetMap = new Map();
  for (let i = 0; i < t.length; i++) {
    const char = t[i];
    targetMap.set(char, (targetMap.get(char) || 0) + 1);
  }

  const required = targetMap.size; // Number of unique characters that must match
  const windowMap = new Map();

  let formed = 0; // Number of unique characters currently matched with required frequency
  let left = 0;
  let right = 0;

  // Tracking minimal substring window: [windowLength, startIndex, endIndex]
  let minLen = Infinity;
  let minStart = 0;

  while (right < s.length) {
    const char = s[right];
    windowMap.set(char, (windowMap.get(char) || 0) + 1);

    // If frequency of current character matches target frequency, increment formed
    if (targetMap.has(char) && windowMap.get(char) === targetMap.get(char)) {
      formed++;
    }

    // Try and contract the window till the point where it ceases to be 'desirable'
    while (left <= right && formed === required) {
      const currentLen = right - left + 1;
      if (currentLen < minLen) {
        minLen = currentLen;
        minStart = left;
      }

      const leftChar = s[left];
      // Reduce count of character leaving the window
      windowMap.set(leftChar, windowMap.get(leftChar) - 1);

      if (targetMap.has(leftChar) && windowMap.get(leftChar) < targetMap.get(leftChar)) {
        formed--;
      }

      left++; // Contract window from the left
    }

    right++; // Expand window to the right
  }

  return minLen === Infinity ? "" : s.substring(minStart, minStart + minLen);
}

4. Pattern 3: The Peak-and-Valley Two Pointer Invariant

Some problems look like dynamic programming or stack problems until you realize that water or area bounds are strictly determined by the shorter of two extremes.


Challenge 3: Trapping Rain Water (LeetCode 42 - Hard / The Toptal Final Round Filter)

Problem: Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.

Elevation Map:
       #
   #   ## #
_#_##_######
[0,1,0,2,1,0,1,3,2,1,2,1] -> Water Trapped = 6

The Intuition:

At any index i, how much water sits on top of bar i?

water[i] = max(0, min(max_height_to_left, max_height_to_right) - height[i])

You could precompute prefix max and suffix max arrays. That takes O(n) time and O(n) space.

Can we do it in O(1) auxiliary space?

Yes. We maintain two pointers: left = 0 and right = height.length - 1, along with leftMax and rightMax.

Here is the key deduction:

  • If height[left] < height[right]:
    • We know for certain that there is a wall on the right that is at least as tall as height[left].
    • Therefore, the water trapped at left is purely dictated by leftMax.
    • If height[left] >= leftMax, update leftMax = height[left].
    • Else, add leftMax - height[left] to total water.
    • Move left++.
  • Otherwise (height[left] >= height[right]):
    • The bottleneck is guaranteed to be on the right.
    • If height[right] >= rightMax, update rightMax = height[right].
    • Else, add rightMax - height[right] to total water.
    • Move right--.

The Code:

/**
 * Time Complexity:  O(n) - Single pass through the array
 * Space Complexity: O(1) - Constant auxiliary memory
 * @param {number[]} height
 * @return {number}
 */
function trap(height) {
  if (!height || height.length < 3) return 0;

  let left = 0;
  let right = height.length - 1;

  let leftMax = 0;
  let rightMax = 0;
  let totalWater = 0;

  while (left < right) {
    if (height[left] < height[right]) {
      // The right side is guaranteed to be taller than height[left].
      // Thus, leftMax is our only bottleneck.
      if (height[left] >= leftMax) {
        leftMax = height[left];
      } else {
        totalWater += leftMax - height[left];
      }
      left++;
    } else {
      // The left side is guaranteed to be taller than or equal to height[right].
      // Thus, rightMax is our only bottleneck.
      if (height[right] >= rightMax) {
        rightMax = height[right];
      } else {
        totalWater += rightMax - height[right];
      }
      right--;
    }
  }

  return totalWater;
}

5. The Toptal Execution Playbook: Writing Code Under Pressure

Toptal testing environments (usually Codility) are unforgiving:

  1. No interactive debugger. You get console.log and standard out.
  2. Hidden test cases. Your score isn't just correctness; it's Performance + Correctness. If your solution is O(n^2) when an O(n) exists, you will score 50% and fail the round.
  3. Extreme inputs: Arrays of length 100,000, all identical numbers, arrays sorted in reverse order, negative numbers, empty arrays, integer overflow edge cases.

The 4-Step Discipline to Follow on Every Linear Problem:

  1. Never write shift() or unshift() inside a loop. Period.
  2. Check empty or 1-element arrays before anything else.
    if (!arr || arr.length < 2) return ...;
  3. Beware of JavaScript's sort() gotcha.
    [10, 2, 5].sort(); // Yields [10, 2, 5] because "10" < "2" lexicographically!
    [10, 2, 5].sort((a, b) => a - b); // Correct: [2, 5, 10]
  4. Name your pointers descriptively. Avoid p1, p2, x, y. Use left, right, slow, fast, windowStart, windowEnd. It keeps you from getting confused when edge cases crop up at minute 42 of your 60-minute interview.

6. Your Practice Gauntlet (Before Part 2)

Do not move to Part 2 until you can write clean, bug-free JavaScript solutions to these without looking at the solutions:

  1. LeetCode 11 (Medium): Container With Most Water (Warm-up for Two Pointers)
  2. LeetCode 167 (Medium): Two Sum II - Input Array Is Sorted (Pointers & bounds)
  3. LeetCode 3 (Medium): Longest Substring Without Repeating Characters (Sliding Window with a Set/Map)
  4. LeetCode 438 (Medium): Find All Anagrams in a String (Fixed-size Sliding Window)
  5. LeetCode 42 (Hard): Trapping Rain Water (Implement the O(1) space two-pointer approach from memory)

What’s Coming Next in Part 2

In Part 2, we leave flat linear arrays behind and enter the memory stack:

  • Linked Lists: Fast & Slow Pointers (Floyd’s Cycle Detection) and in-place reversal tricks.
  • The Monotonic Stack: The secret technique behind solving "Next Greater Element", "Largest Rectangle in Histogram", and daily temperature spikes.
  • Designing an LRU Cache in JavaScript: Why a simple Map technically works, how interviewers test you on it, and how to build a doubly linked list + hash map from raw pointers.

Next step

Tell us what is broken or what should exist.

Send the shape of the problem and any constraints you already know — budget, deadline, the stack you are stuck with. You will get a written reply from the engineer who would do the work, not a sales sequence.