Skip to the notes
JSGroundwork
JSGroundwork handwritten · web dev
✎Playground→⌘Problems↻Review🔥Progress

Chapters

34 chapters
⌕
Beginner10›
B1Complexity analysisB2Arrays & stringsB3HashingB4Two pointersB5Sliding windowB6Binary searchB7Sorting algorithmsB8Stacks & queuesB9Linked listsB10Basic recursion
Intermediate12›
I1TreesI2Tree problems in depthI3Heaps & priority queuesI4Graphs: representationI5Graph problemsI6BacktrackingI7DP: 1DI8DP: 2DI9Greedy algorithmsI10IntervalsI11Bit manipulationI12Matrix problems
Advanced12›
A1Advanced DPA2Union-FindA3Advanced graph algorithmsA4Minimum Spanning TreeA5TriesA6Segment & Fenwick treesA7String algorithmsA8Monotonic stack & queueA9Design problemsA10Advanced backtrackingA11Topological patternsA12Interview strategy
/ search[ ] chaptert top

DSA in JS levels

1Beginner2Intermediate3Advanced

Ready to read

JSJavaScript⑂Git◎Interview prepΣDSA in JSSDSystem Design
More topics15›
</>HTML{ }CSS⚛ReactNNext.jsNeNest.jsTSTypeScriptNoNode.js🐳DockerDBSQL & Databases✓Testing🔒Web Security☁Cloud & DevOps◈GraphQL◆Redis☸Kubernetes
100%
B4

Two pointers

One pass, two positions — how an O(n²) search collapses to O(n).

The shape of the pattern

Two pointers means walking a structure with two indices instead of nested loops. Whenever you're tempted to check every pair (i, j) against each other, ask: does the data have an order I can exploit so the pointers only ever move forward, never backward? If yes, two pointers turns O(n²) pair-checking into a single O(n) pass.

Variant 1 — opposite ends, closing inward

Used on sorted arrays where you need a pair that satisfies some condition.

2 7 11 15 18 24 ↓ left right ↓ → close inward
sum too small → move left right; sum too big → move right left.
// Two Sum on a SORTED array — O(n) time, O(1) space
function twoSumSorted(nums, target) {
  let left = 0, right = nums.length - 1;
  while (left < right) {
    const sum = nums[left] + nums[right];
    if (sum === target) return [left, right];
    if (sum < target) left++;   // need bigger → drop the smaller end
    else right--;               // need smaller → drop the bigger end
  }
  return [-1, -1];
}

Why this is correct, not just fast: because the array is sorted, moving left past the current value can never re-find a pair we already ruled out — every skipped pair genuinely can't work.

Watch it converge, step by step

nums = [2, 7, 11, 15, 18, 24], target = 22:

stepleftrightnums[left]+nums[right]compare to 22move
10 (2)5 (24)26too bigright−−
20 (2)4 (18)20too smallleft++
31 (7)4 (18)25too bigright−−
41 (7)3 (15)22matchreturn [1, 3]

Six elements, but only four comparisons — each one eliminates an entire end of the remaining range, not just one element. That's the O(n) behavior: the pointers together take at most n steps total to meet.

Variant 2 — fast/slow, same direction

Both pointers start at the same end but move at different rates (or one waits while the other scans). This is the shape behind removing duplicates in place, partitioning, and cycle detection in linked lists.

// remove duplicates from a SORTED array, in place — O(n) time, O(1) space
function removeDuplicates(nums) {
  if (nums.length === 0) return 0;
  let slow = 0; // slow = last confirmed-unique position
  for (let fast = 1; fast < nums.length; fast++) {
    if (nums[fast] !== nums[slow]) {
      slow++;
      nums[slow] = nums[fast];
    }
  }
  return slow + 1; // count of unique elements
}

Variant 3 — palindrome / mirror check

function isPalindrome(s) {
  let left = 0, right = s.length - 1;
  while (left < right) {
    if (s[left] !== s[right]) return false;
    left++;
    right--;
  }
  return true;
}
⚠ Two pointers needs an exploitable order The opposite-ends variant only works because the array is sorted — try it on unsorted data and it silently gives wrong answers, not an error. If the input isn't sorted and sorting it doesn't destroy needed information (like original indices), sort first — O(n log n) to enable an O(n) pass is still a huge win over O(n²).
Say it like this → "The array's sorted, so I can use two pointers closing inward — each comparison eliminates one end entirely instead of comparing every pair, which is what gets this from O(n²) down to O(n)."

How to recognize it in an unseen problem

  • The input is sorted (or can be sorted without losing what you need)
  • You're looking for a pair, triplet, or a "does X exist" over combinations
  • A brute force would be nested loops comparing indices against each other
  • The words "sorted array," "pair," or "in-place" appear in the prompt

Three Sum is the natural extension: sort once, then fix one index and run the opposite-ends two-pointer scan on the rest — O(n²) total instead of the O(n³) brute force, because the inner two-sum collapses from a nested loop to a linear scan.

See it move

Step through it and watch why a pointer moves. Every move rules out a whole block of pairs at once — that is the entire reason this beats the nested loop.

Two pointers — find a pair that sums to 22
Current sum

Practice this layer

Opens in the editor — write it, run it, and check it against real tests.

Sort Colors5 tests · intermediateNext Permutation5 tests · advancedTrapping Rain Water5 tests · advancedContainer With Most Water5 tests · intermediateValid Palindrome5 tests · beginnerLongest Palindromic Substring5 tests · advancedTwo Sum II — Input Array Is Sorted5 tests · beginner3Sum5 tests · intermediate4Sum5 tests · advancedSquares of a Sorted Array5 tests · beginnerBackspace String Compare5 tests · intermediateMerge Two Sorted Arrays5 tests · beginner
←previousHashing↑ CovernextSliding window→