Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
Pattern Recognition Cheatsheet (Read This First)
What is Two Pointers?
The Two Pointers pattern uses two pointers to traverse an array or string, typically moving towards each other or in the same direction. It transforms O(n squared) brute force solutions into O(n) optimal ones.Canonical Template Code
This is the boilerplate. Memorize it. Then specialize for the problem.Pattern Recognition Checklist
Before using Two Pointers, ask yourself:Use Two Pointers When
- Array is sorted (or can be sorted)
- Need to find pairs with a property
- In-place modification required
- Need to compare elements from ends
- Want to avoid O(n²) nested loops
Don't Use When
- Need to preserve original order
- Array is unsorted and sorting changes answer
- Need to find subarrays (use Sliding Window)
- Need lookup by value (use HashMap)
When to Use
Sorted Arrays
Palindrome Problems
Merging Arrays
Removing Duplicates
Pattern Variations
1. Opposite Direction (Converging)
Pointers start at both ends and move towards the center.2. Same Direction (Fast & Slow)
Both pointers move in the same direction at different speeds.3. Container With Most Water
4. Valid Palindrome
5. 3Sum
6. Trapping Rain Water
7. Move Zeroes
8. Sort Colors (Dutch National Flag)
Classic Problems
1. Two Sum II (Sorted Array)
1. Two Sum II (Sorted Array)
2. Container With Most Water
2. Container With Most Water
3. Valid Palindrome
3. Valid Palindrome
4. 3Sum
4. 3Sum
5. Trapping Rain Water
5. Trapping Rain Water
Template Code
Common Mistakes
Debugging Checklist
When your Two Pointers solution fails, check:Complexity Quick Reference
| Problem Type | Time | Space | Key Insight |
|---|---|---|---|
| Two Sum (sorted) | O(n) | O(1) | Move based on sum comparison |
| 3Sum | O(n²) | O(1) | Fix one, two-pointer on rest |
| Remove Duplicates | O(n) | O(1) | Slow = write position |
| Container Water | O(n) | O(1) | Move shorter side inward |
| Trap Rain Water | O(n) | O(1) | Track max from both sides |
| Valid Palindrome | O(n) | O(1) | Skip non-alphanumeric |
Interview Problems by Company
- Easy
- Medium
- Hard
| Problem | Company | Key Concept |
|---|---|---|
| Two Sum II | Amazon, Google | Basic converging |
| Valid Palindrome | Meta, Microsoft | Character comparison |
| Move Zeroes | Meta, Amazon | Fast-slow technique |
| Remove Duplicates | All FAANG | In-place modification |
| Squares of Sorted Array | Amazon | Converging with squares |
Interview Tips
How to Explain Your Approach
How to Explain Your Approach
- “I notice the array is sorted, which suggests Two Pointers might work here.”
- “I’ll use two pointers starting at opposite ends.”
- “If the sum is too small, I move left pointer right to increase it.”
- “If too large, I move right pointer left to decrease it.”
- “This gives us O(n) time and O(1) space.”
When Interviewer Says...
When Interviewer Says...
| Interviewer Says | You Should Think |
|---|---|
| ”Can you do better than O(n²)?” | Two Pointers or HashMap |
| ”Can you do it in O(1) space?” | Two Pointers (not HashMap) |
| “The array is sorted” | Definitely Two Pointers! |
| ”Find a pair/triplet” | Two Pointers likely |
| ”Modify array in-place” | Fast-slow pointers |
Common Follow-ups
Common Follow-ups
- “What if there are duplicates?” → Skip duplicates in loop
- “What if no solution exists?” → Return appropriate default
- “Can you handle negative numbers?” → Usually yes, same logic
- “What if array is not sorted?” → Sort first, or use HashMap
Practice Problems
| Problem | Difficulty | Link |
|---|---|---|
| Two Sum II | Easy | LeetCode 167 |
| Valid Palindrome | Easy | LeetCode 125 |
| 3Sum | Medium | LeetCode 15 |
| Container With Most Water | Medium | LeetCode 11 |
| Trapping Rain Water | Hard | LeetCode 42 |
Practice Roadmap
Worked LeetCode Problems
Five canonical problems. For each: signal that triggers Two Pointers, brute force first, optimized solution, and the bugs that fail edge cases.LC 167 — Two Sum II (Input Array Is Sorted)
Problem. Given a 1-indexed sorted array of integers and a target, return the indices of two numbers that add up to the target. Exactly one solution exists. Pattern fit. “Sorted array” + “two numbers summing to target” — the textbook Two Pointers signal. Brute force. Two nested loops, O(n squared) time, O(1) space. Even though the array is sorted, brute force ignores that signal. Optimized with Two Pointers.- Forgetting 1-indexed return. LC 167 wants 1-indexed indices. Off-by-one fails 100% of test cases.
- Using
left <= right. Allows using the same element twice. Returns wrong pair whentarget == 2 * arr[i].
LC 15 — 3Sum
Problem. Given an integer array, find all unique triplets that sum to zero. Pattern fit. “All triplets summing to X” — fix one element, run Two Pointers on the rest. Brute force. Three nested loops, O(n cubed). Use a set of sorted tuples to dedupe — messy. Optimized.- Skipping duplicates after recording, but only on one side. You must skip on both
leftandright, otherwise[-2, 0, 0, 2, 2]produces[[-2, 0, 2], [-2, 0, 2]]. - Forgetting to break when
nums[i] > 0. Not a correctness bug, but a 5x performance loss on large inputs with many positives.
LC 11 — Container With Most Water
Problem. Given an array of heights, find two lines that together with the x-axis form a container holding the most water. Pattern fit. “Pair from sorted-by-position array” + “maximize area” — greedy converging Two Pointers. Brute force. All pairs, O(n squared). Optimized.- Moving the taller side. Misses the optimum on inputs like
[1, 8, 6, 2, 5, 4, 8, 3, 7]. - Updating area after moving pointers. Then you skip the current pair’s contribution. Always update before moving.
LC 125 — Valid Palindrome
Problem. Return true if string is a palindrome considering only alphanumeric characters and ignoring case. Pattern fit. “Palindrome check” — converging Two Pointers from both ends. Brute force. Build a filtered, lowercased string and compare with its reverse. O(n) time, O(n) space. Optimized.- Forgetting
left < rightinside the inner skip loops. Causes index out-of-range when the entire string is non-alphanumeric, e.g.,"....,"or"...."(returning true is correct). - Comparing characters without
.lower(). Fails on"A man, a plan, a canal: Panama".
LC 42 — Trapping Rain Water
Problem. Given an array of bar heights, compute the total water trapped after rain. Pattern fit. “Compute aggregate over array using boundaries from both sides” — converging Two Pointers with running maxes. Brute force. For each index, find max to left and max to right, take min, subtract bar height. O(n squared). Better but still not optimal. Precompute left-max and right-max arrays. O(n) time, O(n) space. Optimized (Two Pointers).height[left] < height[right], we know the right side has at least one bar taller than left_max somewhere to the right. So water at left is determined purely by left_max (the right bound is already known to be at least as tall). Process the bottleneck side first.
Complexity. O(n) time, O(1) space.
Common bugs.
- Initializing
left_max = height[0]then accidentally double-counting position 0. Initialize to 0 and let the first comparison update it. - Comparing
left_maxagainstright_maxinstead ofheight[left]againstheight[right]. The decision is which current bar is shorter, not which max.
Curated LeetCode Practice List
Group your practice by difficulty. Solve every Easy, then move to Medium. Hard problems are useful only after Easy and Medium feel mechanical.Easy (start here, 5-8 problems)
| LC # | Title | Variant tested |
|---|---|---|
| LC 167 | Two Sum II - Input Array Is Sorted | Converging, classic baseline |
| LC 125 | Valid Palindrome | Converging with skip logic |
| LC 344 | Reverse String | Converging swap, in-place |
| LC 26 | Remove Duplicates from Sorted Array | Fast/slow, in-place rewrite |
| LC 283 | Move Zeroes | Fast/slow with swap to preserve order |
| LC 977 | Squares of a Sorted Array | Converging, write to result from end |
| LC 88 | Merge Sorted Array | Reverse converging, in-place from end |
| LC 392 | Is Subsequence | Two pointers across two arrays |
Medium (the meat of interview prep, 6-8 problems)
| LC # | Title | Variant tested |
|---|---|---|
| LC 15 | 3Sum | Sort + fix-one + converging, dedupe |
| LC 16 | 3Sum Closest | Same as 3Sum but track closest sum |
| LC 11 | Container With Most Water | Greedy converging on heights |
| LC 75 | Sort Colors | Three pointers (Dutch flag) |
| LC 80 | Remove Duplicates from Sorted Array II | Fast/slow with slow - 2 trick |
| LC 18 | 4Sum | Fix-two + converging, generalizes 3Sum |
| LC 845 | Longest Mountain in Array | Two pointers expanding from peaks |
| LC 1248 | Count Subarrays with K Odd Numbers | Two-pointer + sliding (atMost trick) |
Hard (after you have grinded the above, 5-6 problems)
| LC # | Title | Variant tested |
|---|---|---|
| LC 42 | Trapping Rain Water | Converging with running maxes |
| LC 4 | Median of Two Sorted Arrays | Binary search disguised as two pointers |
| LC 76 | Minimum Window Substring | Two pointers + frequency map (sliding window flavor) |
| LC 632 | Smallest Range Covering K Lists | K pointers via min-heap |
| LC 581 | Shortest Unsorted Continuous Subarray | Two passes from both ends |
| LC 1782 | Count Pairs Of Nodes | Two pointers on sorted degree counts |
Interview Questions
Deep-dive questions that test real understanding of the Two Pointers pattern, not just pattern matching. Expect interviewers to push past your initial answer.Q1: Why does moving the shorter line inward work in Container With Most Water? Prove it doesn't skip the optimal answer.
Q1: Why does moving the shorter line inward work in Container With Most Water? Prove it doesn't skip the optimal answer.
- The area is
min(height[left], height[right]) * (right - left). When we move a pointer inward, the width always decreases by 1. The only way to get a larger area is if the height increases enough to compensate. - Suppose
height[left] < height[right]. If we movedrightinward instead, the new height is still capped byheight[left](sincemin(anything, height[left]) <= height[left]), AND the width decreased. So area strictly decreases or stays the same. We can safely discard all pairs involving currentleftwith anyright' < right. - This is essentially an elimination argument: by moving the shorter side, we only eliminate pairs that provably cannot beat the current best. We never skip a pair that could be optimal.
- Complexity: O(n) time, O(1) space. Each pointer moves at most n times total.
- What if there are many lines with the same height? Does the algorithm degenerate? What is the worst-case number of comparisons? (Answer: still O(n), each pointer moves at most n/2 times regardless of duplicates.)
- Can you modify this to return ALL pairs of lines whose area is within 10% of the maximum? Does the two-pointer approach still work, or do you need a different strategy? (Answer: two-pointer no longer works cleanly because the elimination argument breaks — you would need to explore more pairs, potentially O(n^2) in the worst case, or use a sorted/stack-based approach.)
Q2: Given an unsorted array of integers, find if there exist two elements whose sum equals a target. What approach do you use and why?
Q2: Given an unsorted array of integers, find if there exist two elements whose sum equals a target. What approach do you use and why?
- For an unsorted array, the right answer is a HashMap/HashSet, not two pointers. Insert each element and check if
target - currentexists. O(n) time, O(n) space. - Two pointers requires sorting first, which is O(n log n) and also destroys original indices — if the problem asks for indices (like LeetCode Two Sum), you lose that information unless you store original indices separately.
- The trade-off: HashMap gives O(n) time but O(n) space. Sorting + two pointers gives O(n log n) time but O(1) space (if you do not need indices). Choose based on constraints.
- In an interview, I would state: “Since the array is unsorted and we need O(n) time, I will use a HashMap. If the interviewer constrains me to O(1) space and does not need original indices, I would sort then use two pointers.”
- What if memory is extremely constrained (embedded system, 2MB RAM) but the array has 100 million elements on disk? Now sorting + two pointers (external sort) might beat the HashMap approach. Walk me through that trade-off.
- What if there are many duplicate values? Does the HashMap approach need modification? (Answer: depends on whether you need all pairs or just one. For all pairs, you need to store counts or lists of indices.)
Q3: In the 3Sum problem, why do we sort the array first? What breaks if we skip sorting?
Q3: In the 3Sum problem, why do we sort the array first? What breaks if we skip sorting?
- Sorting serves two critical purposes in 3Sum:
- Enables two-pointer scanning: After fixing one element, the remaining subarray is sorted, so converging pointers can systematically search in O(n) instead of O(n^2).
- Makes duplicate skipping trivial: In a sorted array, duplicates are adjacent. You just check
if nums[i] == nums[i-1]: continue. Without sorting, you would need a HashSet to track which triplets you have already seen, which is messier and uses O(k) extra space for k results.
- Without sorting, you would need a HashMap-based approach: for each pair
(i, j), check if-(nums[i] + nums[j])exists. This is O(n^2) time (same as sorted approach) but requires O(n) extra space for the map and a set of sorted tuples to deduplicate results, which is error-prone. - The sorting cost is O(n log n), and the two-pointer scan is O(n^2), so sorting does not change the overall O(n^2) complexity. It is essentially “free.”
- If the problem changes to 4Sum, how does the complexity change? Can you still use two pointers? (Answer: yes, fix two elements O(n^2), two-pointer scan O(n) for each = O(n^3) total. The pattern generalizes: k-Sum is O(n^(k-1)) with sorting + two pointers.)
- What if the array is already sorted and has 10 million elements? What practical optimizations would you add? (Answer: early termination — if
nums[i] > 0, no triplet starting here can sum to zero. Ifnums[i] + nums[i+1] + nums[i+2] > 0, break. These prune aggressively.)
Q4: Explain the difference between `left < right` and `left <= right` as loop conditions. When does picking the wrong one cause bugs?
Q4: Explain the difference between `left < right` and `left <= right` as loop conditions. When does picking the wrong one cause bugs?
left < rightmeans the pointers must have at least one element between them or be adjacent. Whenleft == right, the loop exits. Use this when you are comparing/processing pairs of distinct elements (e.g., Two Sum, palindrome check, Container With Most Water).left <= rightmeans even when both pointers point to the same element, you still process it. Use this when a single element might still need processing (e.g., Dutch National Flag / Sort Colors, binary search).- Concrete bug example: In the palindrome check, using
left <= rightwould not cause a bug (checking a single middle character against itself is fine), but in Two Sum, usingleft <= rightcould cause you to use the same element twice — returning[i, i]as a valid pair whenarr[i] * 2 == target. - The mental model I use: Ask yourself: “When
left == right, is there still meaningful work to do, or have I already considered everything?” If the answer is no, use<. If yes, use<=.
left < right for two pointers.” This shows the candidate does not think about why, just memorizes. Another red flag: being unable to produce a concrete input where the wrong condition causes a failure.Follow-ups:- In binary search, when do you use
left < rightvsleft <= rightvsleft + 1 < right? What bug does each variant prevent? (Tests whether the candidate connects two-pointer reasoning to binary search, which is fundamentally a two-pointer technique.) - Walk me through what happens with the input
[1, 2, 3, 4, 5]and target6using Two Sum withleft <= right. Where exactly does it break? (Answer: whenleft = right = 2, it would return[2, 2]claiming3 + 3 = 6, but you cannot use the same element twice.)
Q5: How does the two-pointer solution for Trapping Rain Water work, and why is it correct? What alternatives exist and when would you prefer them?
Q5: How does the two-pointer solution for Trapping Rain Water work, and why is it correct? What alternatives exist and when would you prefer them?
- Core invariant: At any position, the water trapped equals
min(max_left, max_right) - height[i]. The two-pointer approach works because we always process from the side with the smaller known maximum. Ifleft_max < right_max, we know the water atleftis determined byleft_maxregardless of what lies between the pointers (sinceright_maxis already larger, and anything in between could only increase the right maximum further). - Why it is correct: We process the “bottleneck side” first. When
left_max < right_max, the constraining factor at positionleftisleft_max, notright_max. So we can safely compute water atleftwithout knowing the exactright_max— we just know it is at least as large. - Three approaches compared:
| Approach | Time | Space | When to prefer |
|---|---|---|---|
| Prefix/suffix max arrays | O(n) | O(n) | Easiest to understand and code correctly in an interview. Good default. |
| Two pointers | O(n) | O(1) | When the interviewer explicitly asks for O(1) space or you are confident in the invariant. |
| Monotonic stack | O(n) | O(n) | When the problem is framed as “horizontal layers” or you need to process bars left-to-right only (streaming). |
- My interview strategy: I would start by explaining the prefix/suffix approach (simpler, harder to get wrong), then optimize to two pointers if asked for O(1) space.
- What if this is a 2D grid instead of 1D bars (LC 407 — Trapping Rain Water II)? Does two-pointer generalize? (Answer: no. You need a priority queue / BFS approach processing the boundary inward. The 1D invariant does not extend to 2D because water can escape in multiple directions.)
- If the input is a stream of heights arriving one at a time and you need a running total of trapped water, which approach adapts best? (Answer: the monotonic stack approach adapts most naturally to streaming, since it processes left-to-right. Two pointers requires the full array upfront.)
Q6: Given a string, find the length of the longest substring without repeating characters. Is two pointers the right approach?
Q6: Given a string, find the length of the longest substring without repeating characters. Is two pointers the right approach?
- This is a sliding window problem, not a classic two-pointer problem. The distinction matters:
- Two pointers typically work on sorted data or compare elements at both ends. Pointer movement is driven by comparison logic (sum too big/small, palindrome match/mismatch).
- Sliding window maintains a window with an invariant (in this case, “no repeating characters”) and expands/shrinks to maintain it. Pointer movement is driven by window validity.
- The approach: expand the right pointer to grow the window. When a duplicate is found, shrink from the left until the window is valid again. Use a HashSet or HashMap to track characters in the current window.
- Complexity: O(n) time (each character is added and removed from the set at most once), O(min(n, alphabet_size)) space.
- What if instead of “no repeating characters” the constraint is “at most K distinct characters”? How does your approach change? (Answer: same sliding window, but the HashMap tracks character counts. Shrink when distinct count exceeds K.)
- Can you solve this with O(1) space if the input is guaranteed to be lowercase English letters only? (Answer: yes, use a fixed-size array of 26 instead of a HashMap. Space is O(1) since it does not grow with input size.)
Q7: You have a sorted array with duplicates. Remove duplicates in-place so each element appears at most twice. How does this change the fast-slow pointer approach?
Q7: You have a sorted array with duplicates. Remove duplicates in-place so each element appears at most twice. How does this change the fast-slow pointer approach?
- The standard remove-duplicates uses
slowto track the write position and skips whenarr[fast] == arr[slow]. For “at most twice,” we need to comparearr[fast]againstarr[slow - 1](two positions back) instead ofarr[slow]. - Key insight: If
arr[fast] == arr[slow - 1], we already have two copies of this value, so skip. Otherwise, write it. - Generalization: For “at most K duplicates,” compare
arr[fast]againstarr[slow - (K-1)]. This is a clean generalization that interviewers love. - Complexity: O(n) time, O(1) space. Single pass.
slow - 2 comparison trick. An even worse red flag: not realizing the array must be sorted for this approach to work, or wanting to use extra space.Follow-ups:- What if the array is not sorted? Can you still remove elements appearing more than twice in O(n) time? (Answer: you need a HashMap to count frequencies, then a second pass to filter. O(n) time but O(n) space. Two pointers alone cannot solve this on unsorted input.)
- The interviewer says: “Now do it where each element appears at most K times, and K is a parameter.” Write it generalized. (Answer: shown above. Tests whether the candidate can abstract a pattern into a parameter.)
Q8: Given an array of n integers and a target, find the number of unique quadruplets that sum to the target. What is the best approach?
Q8: Given an array of n integers and a target, find the number of unique quadruplets that sum to the target. What is the best approach?
- Approach: Sort the array. Use two nested loops to fix the first two elements. For the remaining two, use converging two pointers. This gives O(n^3) time.
- Duplicate handling: Skip duplicates at each level — for the first loop, second loop, and within the two-pointer scan. Same logic as 3Sum but applied at more levels.
- Critical pruning optimizations (what separates good from great):
- Early termination (too small): If
nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target, break the outer loop — all remaining sums are larger. - Early termination (too large): If
nums[i] + nums[n-3] + nums[n-2] + nums[n-1] < target, skip thisi— even the largest possible sum with thisiis too small. - Same pruning applies at the second loop level.
- Early termination (too small): If
- Complexity: O(n^3) time worst case, O(1) space (excluding output). With pruning, practical performance is much better.
- Why not HashMap? You could use a HashMap of pair sums for O(n^2) time, but handling duplicates becomes extremely messy and the space is O(n^2). For an interview, the sorted + nested two-pointer approach is cleaner and safer to implement.
- What is the general time complexity for K-Sum using this approach? (Answer: O(n^(K-1)). You nest K-2 loops and use two pointers for the innermost pair.)
- For very large arrays where O(n^3) is too slow, can you think of a meet-in-the-middle approach? What is its complexity? (Answer: compute all pair sums in O(n^2), sort them, then use two pointers on the pair-sum array to find complementary pairs. Time is O(n^2 log n), but duplicate handling and reconstructing original indices is tricky.)
Q9: Given an unsorted array, find the shortest subarray that, if sorted, would make the entire array sorted. Which technique applies?
Q9: Given an unsorted array, find the shortest subarray that, if sorted, would make the entire array sorted. Which technique applies?
- Approach: Use two pointers, but not the classic converging pair. Scan from the left to find the rightmost element that is “out of order” (smaller than some element to its left). Scan from the right to find the leftmost element that is “out of order” (larger than some element to its right).
- Detailed steps:
- Traverse left to right, tracking the running maximum. Whenever
nums[i] < max_so_far, this position needs to be inside the sorted subarray. Track the rightmost such position asright_bound. - Traverse right to left, tracking the running minimum. Whenever
nums[i] > min_so_far, this position needs to be inside the sorted subarray. Track the leftmost such position asleft_bound. - The answer is
right_bound - left_bound + 1. If no such bounds exist, the array is already sorted (return 0).
- Traverse left to right, tracking the running maximum. Whenever
- Why this works: Any element that violates the monotonicity when scanning from either end MUST be included in the subarray that needs sorting.
- Complexity: O(n) time, O(1) space. Two passes.
- Can you prove that sorting just this subarray is sufficient? What if an element outside the subarray is affected? (Answer: by construction, all elements left of
left_boundare smaller than everything in the subarray and already sorted, and all elements right ofright_boundare larger and already sorted. So sorting the subarray restores global order.) - How would you adapt this if the requirement changes to “find the shortest subarray to REVERSE (not sort) to make the array sorted”? (Answer: much harder. The subarray to reverse must form a decreasing sequence that, when flipped, creates a valid sorted array. This requires checking that the reversed subarray merges correctly with both ends.)
Q10: You need to merge two sorted arrays into the first array which has enough trailing space. The naive approach copies to a new array. Can you do better?
Q10: You need to merge two sorted arrays into the first array which has enough trailing space. The naive approach copies to a new array. Can you do better?
- The key insight: If you merge from the front, you overwrite elements in
nums1that you still need. By merging from the back (placing the largest elements first), you fill the trailing empty space and never overwrite unprocessed elements. - Use three pointers:
p1at the last real element ofnums1,p2at the last element ofnums2, andwriteat the very end ofnums1. Comparenums1[p1]andnums2[p2], place the larger one atwrite, and decrement. - Edge case: If
p2finishes first,nums1’s remaining elements are already in place. Ifp1finishes first, copy the rest ofnums2. The second case is the one most candidates forget. - Complexity: O(m + n) time, O(1) space. Truly in-place.
- Why do we not need to handle the case where
p1still has elements remaining? (Answer: those elements are already in their correct positions innums1. Since we are writing from the back andnums1is sorted, any remainingnums1elements are the smallest and are already at the front.) - What if instead of two sorted arrays, you had K sorted arrays and needed to merge them all? Does the two-pointer approach scale, or do you need a fundamentally different technique? (Answer: for K arrays, use a min-heap / priority queue of size K. Two pointers does not generalize to K-way merge efficiently — pairwise merging would be O(nK) vs O(n log K) with a heap.)