From f202c3a0a100cd0cad000026e977a03d88e4612b Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Tue, 6 Oct 2026 13:16:45 -0400 Subject: [PATCH] =?UTF-8?q?docs(docs):=20add=20two=E2=80=91pointer=20guide?= =?UTF-8?q?=20with=20decision=20tree=20and=20illustrations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../two-pointers/index.mdx | 157 ++++++++++++++++++ .../public/opposite-direction-bottleneck.svg | 41 +++++ .../opposite-direction-monotonic-sum.svg | 76 +++++++++ .../public/opposite-direction-symmetry.svg | 48 ++++++ .../docs/public/same-direction-compaction.svg | 80 +++++++++ apps/docs/public/same-direction-fixed-gap.svg | 97 +++++++++++ .../public/same-direction-relative-speed.svg | 89 ++++++++++ .../public/two-pointers-decision-tree.svg | 68 ++++++++ 8 files changed, 656 insertions(+) create mode 100644 apps/docs/public/opposite-direction-bottleneck.svg create mode 100644 apps/docs/public/opposite-direction-monotonic-sum.svg create mode 100644 apps/docs/public/opposite-direction-symmetry.svg create mode 100644 apps/docs/public/same-direction-compaction.svg create mode 100644 apps/docs/public/same-direction-fixed-gap.svg create mode 100644 apps/docs/public/same-direction-relative-speed.svg create mode 100644 apps/docs/public/two-pointers-decision-tree.svg diff --git a/apps/docs/content/(01-linear-structures)/two-pointers/index.mdx b/apps/docs/content/(01-linear-structures)/two-pointers/index.mdx index b7118e0..d18f210 100644 --- a/apps/docs/content/(01-linear-structures)/two-pointers/index.mdx +++ b/apps/docs/content/(01-linear-structures)/two-pointers/index.mdx @@ -3,3 +3,160 @@ title: Two Pointers description: 'Two indices move through the data from different positions and meet at the answer.' hidden: true --- + +Most interview problems in this section fall into one of four families: + +- **Same direction pointers**: one pointer reads, one pointer writes, or one pointer stays behind another while scanning left to right. +- **Opposite direction pointers**: one pointer starts on the left, one on the right, and each move eliminates impossible answers. +- **Sliding window**: the two pointers define a contiguous interval, and you maintain some property of that interval as the window expands and shrinks. +- **Fast and slow pointers**: both pointers move through a linked structure, but at different speeds or with a fixed gap. + +![](../../../public/two-pointers-decision-tree.svg) + +## Same Direction + +Same-direction pointer problems usually scan from left to right, but the two pointers do different jobs. + +Usually: + +- one pointer reads the next value from the original input +- one pointer marks where the next kept value should go + +Sometimes both pointers move forward together, but one pointer stays behind the other to preserve a gap or a relationship. + +### When this family applies + + +Reach for same-direction pointers when the problem asks you to: + +- rewrite an array in-place +- remove or compress elements without extra memory +- keep only the values that satisfy some condition +- maintain a fixed gap between two forward-moving pointers + +### Core Invariant + +The invariant in this family is usually about the prefix you have already processed. + +Typical invariant: + +> "Everything before the write pointer is already in the correct final form." + +That is what makes these problems feel different from opposite-direction scans. You are not searching from both sides. You are building the answer progressively as you sweep through the input. + +### Subtypes in this section + +#### 1. Compaction / Overwrite + +The slow pointer marks the next output position. The fast pointer scans the input. When the current value should be kept, copy it forward and advance the slow pointer. + +![](../../../public/same-direction-compaction.svg) + +#### 2. Relative Speed + +Both pointers move forward, but one moves twice as fast. The invariant is about their distance and how much of the structure each pointer has consumed. + +![](../../../public/same-direction-relative-speed.svg) + +#### 3. Fixed Gap + +Both pointers move forward while preserving a gap. The invariant is not about a rewritten prefix. It is about the distance between the pointers. + +![](../../../public/same-direction-fixed-gap.svg) + +### What changes across problems + +The family stays the same, but the invariant changes slightly: + +- in array compaction, the invariant is about the prefix being correct +- in fast/slow midpoint problems, the invariant is about relative speed +- in fixed-gap problems, the invariant is about distance + +So the unifying idea is not "both pointers move right." The unifying idea is: + +**"the structure to the left of the pointers already has a known meaning"** + +### What to watch out for + +Common mistakes: + +- Updating the write pointer before copying the current value. +- Forgetting exactly what region is already valid. +- Losing track of whether the invariant is about a prefix, a gap, or relative speed. + +As you go through the following problems, pay attention to one question: + +**What does the region before the slow pointer mean right now?** + +## Opposite Direction + +Opposite-direction pointer problems place one pointer on the left and one on the right, then move inward. + +The hard part is never the loop itself. **The hard part is justifying which pointer is safe to move.** + +### When this family applies + +Reach for opposite-direction pointers when the problem asks you to: + +- the input is sorted, or can be made monotonic by sorting +- you are comparing a left choice and a right choice +- the problem has symmetry +- moving one side lets you eliminate a set of impossible answers + +### Core Invariant + +The invariant in this family is usually: + +> "All candidate answers outside the current `[left, right]` range have already been ruled out." + +That means each move must come with a proof. If you move a pointer without knowing why the discarded cases cannot contain the optimum, the algorithm is just guessing. + +### Subtypes in this section + +#### 1. Monotonic sum elimination + + +Because the array is sorted: + +- if the sum is too small, moving the left pointer right is the only useful move +- if the sum is too large, moving the right pointer left is the only useful move + +Sorted order gives you a monotonic rule. + +![](../../../public/opposite-direction-monotonic-sum.svg) + +#### 2. Symmetry checking + +Here the pointers compare mirrored positions. The movement rule is mostly "move both," except when you must skip punctuation or spaces. + +![](../../../public/opposite-direction-symmetry.svg) + +#### 3. Bottleneck elimination + +This one is important because it looks different from the other two. The key idea is that the shorter wall limits the area. If you keep that shorter wall and decrease the width, you cannot improve the answer. So it is safe to discard that wall. + +![](../../../public/opposite-direction-bottleneck.svg) + +### Why this is not a sliding window + +Opposite-direction scans do not maintain an interval property like "this window is valid." + +Instead, each step does one of these: + +- rules out sums that are too small or too large +- checks mirrored characters +- discards a bottleneck boundary + +The interval itself is just the remaining search space. + +### What to watch out for + +Common mistakes: + +- Using two pointers on an unsorted pair problem without first creating monotonicity. +- Moving the taller wall in Container With Most Water without an elimination proof. +- Calling the method sliding window just because two boundaries exist. + +As you read the problems in this family, keep asking: + +**What exact set of candidate answers did this pointer move eliminate?** diff --git a/apps/docs/public/opposite-direction-bottleneck.svg b/apps/docs/public/opposite-direction-bottleneck.svg new file mode 100644 index 0000000..9ddabb8 --- /dev/null +++ b/apps/docs/public/opposite-direction-bottleneck.svg @@ -0,0 +1,41 @@ + + + + + + Opposite Direction: Bottleneck Elimination + Container With Most Water: discard the shorter wall. + + + + + + 8 + + L + + + + + + + + + + 3 + + R + + + + + + + The bottleneck rule: + Since height[R] < height[L], the only way to find + a larger area is to discard R and move left. + + + + + diff --git a/apps/docs/public/opposite-direction-monotonic-sum.svg b/apps/docs/public/opposite-direction-monotonic-sum.svg new file mode 100644 index 0000000..b9aeb65 --- /dev/null +++ b/apps/docs/public/opposite-direction-monotonic-sum.svg @@ -0,0 +1,76 @@ + + + + + + Opposite Direction: Monotonic Sum + Two Sum Sorted: target = 16. Adjust pointers based on sum. + + + + 1. Sum is too large (2 + 15 = 17) + + + + + + 2 + + L + + + 5 + + + 8 + + + 11 + + + + 15 + + R + + + 17 > 16: R must move left. + + + + + + + Move Right pointer left + + + + + 2. Sum is too small (2 + 11 = 13) + + + + + 2 + + L + + + 5 + + + 8 + + + 11 + + R + + + 15 + + + 13 < 16: L must move right. + + + diff --git a/apps/docs/public/opposite-direction-symmetry.svg b/apps/docs/public/opposite-direction-symmetry.svg new file mode 100644 index 0000000..5130325 --- /dev/null +++ b/apps/docs/public/opposite-direction-symmetry.svg @@ -0,0 +1,48 @@ + + + + + + Opposite Direction: Symmetry Checking + Valid Palindrome: Comparing mirrored positions from outside in. + + + + + + r + + + a + + + c + + CHECK + + + e + + CHECK + + + a + + + r + + + + L + + + R + + + + OUTER SYMMETRY VALIDATED + + + + Oops! 'c' != 'e', not a palindrome. + diff --git a/apps/docs/public/same-direction-compaction.svg b/apps/docs/public/same-direction-compaction.svg new file mode 100644 index 0000000..cb68184 --- /dev/null +++ b/apps/docs/public/same-direction-compaction.svg @@ -0,0 +1,80 @@ + + + + + + Same-Direction Pointers: Compaction + Building a correct prefix while scanning the input. + + + + 1. Before copying + + + + + + 1 + + + 2 + + + + 0 + + write + + + + 4 + + read + + + 3 + + + + CORRECT PREFIX + + + + + + + Copy read value to write position + + + + + 2. After copying and advancing + + + + + 1 + + + 2 + + + 4 + + + + 4 + + write + + + + 3 + + read + + + PREFIX GREW + + + diff --git a/apps/docs/public/same-direction-fixed-gap.svg b/apps/docs/public/same-direction-fixed-gap.svg new file mode 100644 index 0000000..ac6ac3e --- /dev/null +++ b/apps/docs/public/same-direction-fixed-gap.svg @@ -0,0 +1,97 @@ + + + + + + Same-Direction: Fixed Gap + Maintaining a distance of N while traversing. + + + + 1. Gap established (N=2) + + + + + + left + + + right + + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + + + + + + + + GAP OF 2 + + + + + + + Move both together + + + + + 2. Right hits the end + + + + + + left + + + right + + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + + + + + + + + STILL GAP OF 2 + + Left is now N steps from the end. + + diff --git a/apps/docs/public/same-direction-relative-speed.svg b/apps/docs/public/same-direction-relative-speed.svg new file mode 100644 index 0000000..67bd81b --- /dev/null +++ b/apps/docs/public/same-direction-relative-speed.svg @@ -0,0 +1,89 @@ + + + + + + Same-Direction: Relative Speed + Fast pointer moves at 2x speed to find the midpoint. + + + + 1. After one move + + + + + + slow + + + fast + + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + + + + + + + + + + + Move: slow +1, fast +2 + + + + + 2. Reached the middle + + + + + + slow + + + fast + + + + 1 + + + 2 + + + 3 + + + 4 + + + 5 + + + + + + + + Midpoint found! + + diff --git a/apps/docs/public/two-pointers-decision-tree.svg b/apps/docs/public/two-pointers-decision-tree.svg new file mode 100644 index 0000000..7a624a2 --- /dev/null +++ b/apps/docs/public/two-pointers-decision-tree.svg @@ -0,0 +1,68 @@ + + + + + + Two Pointers Decision Rule + + + + New Problem + + + + + Am I maintaining a contiguous interval? + (Subarray, substring, window property) + + + Yes + + Sliding Window + + + No + + + + Can I safely eliminate one side? + (Sorted, symmetric, bottleneck) + + + Yes + + Opposite Dir + + + No + + + + Am I rewriting or filtering in place? + (Remove dups, move zeros, partition) + + + Yes + + Same Dir + + + No + + + + Am I reasoning about relative position? + (Midpoint, cycle, fixed gap) + + + Yes + + Fast & Slow + + + No + + + + Other Techniques +