← Back to Course

Top Interview Questions for Two Pointers

Two Pointers Technique – 6 Common Problem Patterns

Two Pointers Technique

6 Common Problem Patterns in Coding Interviews

Introduction

Two-pointers is an effective technique where two indices (or pointers) traverse the data structure in tandem to achieve a goal. Below, we explore six popular LeetCode problems across Easy, Medium, and Hard difficulties that use two pointers, each highlighting a different variant of the pattern. For each variant, we outline the modification to the basic two-pointer approach, provide a representative LeetCode example, walk through the solution step by step, and include code for clarity.

1. Find First Occurrence in Duplicates (Skip Adjacent Duplicates)

Modified Template: One pointer iterates, a second "write" pointer lags to build the result in-place. Since the array is sorted, duplicates are adjacent. Advance write-pointer only for new distinct elements.

Example Problem: LeetCode 26. Remove Duplicates from Sorted Array – Easy. Given a sorted array, remove duplicates in-place so each element appears once; return new length.

Walkthrough (nums = [0,0,1,1,1,2,2,3]):
  1. i = 0 (write), j = 1 (read). nums[0] is unique.
  2. j=1: nums[1](0) == nums[0](0). Skip.
  3. j=2: nums[2](1) != nums[0](0). i++ (i=1). nums[1]=nums[2](1). Array: [0,1,...].
  4. j=3,4: Duplicates of nums[1](1). Skip.
  5. j=5: nums[5](2) != nums[1](1). i++ (i=2). nums[2]=nums[5](2). Array: [0,1,2,...].
  6. j=6: Duplicate of nums[2](2). Skip.
  7. j=7: nums[7](3) != nums[2](2). i++ (i=3). nums[3]=nums[7](3). Array: [0,1,2,3,...].
End. i=3. Return i+1 = 4. Array's first 4: [0,1,2,3].
def removeDuplicates(nums: list[int]) -> int:
    if not nums: return 0
    i = 0  # index of last unique element
    for j in range(1, len(nums)):
        if nums[j] != nums[i]:
            i += 1
            nums[i] = nums[j]
    return i + 1

2. Reverse Vowels in a String (Two Pointers at Ends with Conditional Skip)

Modified Template: Two pointers start at opposite ends, moving inward. Each pointer moves until it hits a vowel (skipping consonants), then swap vowels.

Example Problem: LeetCode 345. Reverse Vowels of a String – Easy.

Walkthrough (s = "hello"):
  1. left=0 ('h'), right=4 ('o').
  2. Move left: s[0]='h' (skip) -> left=1. s[1]='e' (vowel).
  3. Move right: s[4]='o' (vowel).
  4. Swap chars[1] and chars[4]. String becomes "holle".
  5. left++ (to 2), right-- (to 3). left < right.
  6. Move left: s[2]='l' (skip) -> left=3. s[3]='l' (skip) -> left=4.
  7. Move right: s[3]='l' (skip) -> right=2.
  8. Now left=4, right=2. left >= right, loop ends.
Result: "holle".
def reverseVowels(s: str) -> str:
    vowels = set("aeiouAEIOU")
    chars = list(s)
    left, right = 0, len(chars) - 1
    while left < right:
        while left < right and chars[left] not in vowels: left += 1
        while left < right and chars[right] not in vowels: right -= 1
        if left < right:
            chars[left], chars[right] = chars[right], chars[left]
            left += 1
            right -= 1
    return "".join(chars)

3. Two Pointers for Triplet Sum (3Sum Problem)

Modified Template: Sort array. Outer loop fixes one number. Inner two pointers (left/right on remaining part) find two numbers summing to target. Skip duplicates.

Example Problem: LeetCode 15. 3Sum – Medium. Find unique triplets [a,b,c] where a+b+c=0.

Walkthrough (nums = [-1,0,1,2,-1,-4] -> sorted: [-4,-1,-1,0,1,2]):
  1. i=0 (val -4): Target sum for pair = 4. left=1, right=5.
    • (-4) + (-1) + 2 = -3 (low). left++. ... Loop ends, no triplet.
  2. i=1 (val -1): Target sum for pair = 1. left=2, right=5.
    • (-1) + (-1) + 2 = 0. Found [-1,-1,2]. left++, right--. Skip duplicates.
    • left=3(0), right=4(1): (-1) + 0 + 1 = 0. Found [-1,0,1]. left++, right--. left >= right.
  3. i=2 (val -1): Same as nums[i-1]. Skip.
  4. ... (Outer loop continues)
Result: [[-1,-1,2], [-1,0,1]].
def threeSum(nums: list[int]) -> list[list[int]]:
    nums.sort()
    res = []
    n = len(nums)
    for i in range(n - 2):
        if i > 0 and nums[i] == nums[i-1]: continue # skip duplicates
        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total == 0:
                res.append([nums[i], nums[left], nums[right]])
                left += 1; right -= 1
                while left < right and nums[left] == nums[left-1]: left += 1
                while left < right and nums[right] == nums[right+1]: right -= 1
            elif total < 0: left += 1
            else: right -= 1
    return res

4. Container With Most Water (Max Area by Converging Pointers)

Modified Template: Two pointers at ends of array (line heights). Calculate area. Move pointer at shorter line inward (hoping for taller line to increase area).

Example Problem: LeetCode 11. Container With Most Water – Medium.

Walkthrough (height = [1,8,6,2,5,4,8,3,7]):
  1. left=0(h=1), right=8(h=7). Width=8. Area=min(1,7)*8 = 8. MaxArea=8. Shorter is left (1<7), left++.
  2. left=1(h=8), right=8(h=7). Width=7. Area=min(8,7)*7 = 49. MaxArea=49. Shorter is right (7<8), right--.
  3. left=1(h=8), right=7(h=3). Width=6. Area=min(8,3)*6 = 18. MaxArea=49. Shorter is right (3<8), right--.
  4. ... (Loop continues until left >= right)
Result: MaxArea = 49.
def maxArea(height: list[int]) -> int:
    left, right = 0, len(height) - 1
    max_area = 0
    while left < right:
        h_left, h_right = height[left], height[right]
        width = right - left
        curr_area = min(h_left, h_right) * width
        max_area = max(max_area, curr_area)
        if h_left < h_right: left += 1
        else: right -= 1
    return max_area

5. Trapping Rain Water (Two Pointers with Dynamic Max Tracking)

Modified Template: Two pointers at ends. Maintain leftMax and rightMax (highest walls seen from left/right). Move pointer on side with lower max height inward. Accumulate water based on min(leftMax, rightMax) - current_height.

Example Problem: LeetCode 42. Trapping Rain Water – Hard.

Walkthrough (height = [4,2,0,3,2,5]):
  1. l=0, r=5. lMax=h[0]=4, rMax=h[5]=5. water=0.
  2. lMax(4) <= rMax(5): Move l. l=1. lMax=max(4,h[1]=2)=4. water += max(0, 4-2) = 2.
  3. lMax(4) <= rMax(5): Move l. l=2. lMax=max(4,h[2]=0)=4. water += max(0, 4-0) = 4. Total water=6.
  4. lMax(4) <= rMax(5): Move l. l=3. lMax=max(4,h[3]=3)=4. water += max(0, 4-3) = 1. Total water=7.
  5. lMax(4) <= rMax(5): Move l. l=4. lMax=max(4,h[4]=2)=4. water += max(0, 4-2) = 2. Total water=9.
  6. l=4, r=5. Loop condition l < r is now false (or next iter l becomes 5). Loop ends.
Result: Total water = 9.
def trap(height: list[int]) -> int:
    if not height: return 0
    n = len(height)
    left, right = 0, n - 1
    leftMax, rightMax = height[left], height[right]
    water = 0
    while left < right:
        if leftMax <= rightMax:
            left += 1
            leftMax = max(leftMax, height[left])
            water += max(0, leftMax - height[left])
        else:
            right -= 1
            rightMax = max(rightMax, height[right])
            water += max(0, rightMax - height[right])
    return water

6. Minimum Window Substring (Sliding Window with Two Pointers)

Modified Template: Sliding window. Right pointer expands window. Left pointer contracts when condition (all target chars present) is met. Track char frequencies.

Example Problem: LeetCode 76. Minimum Window Substring – Hard. Given s and t, find min window in s containing all chars of t.

Walkthrough (s="ADOBECODEBANC", t="ABC"):
  1. need={'A':1,'B':1,'C':1}, required=3, have=0. window_counts={}. res=(inf,N,N). left=0.
  2. right=0..5 ("ADOBEC"): `have` becomes 3. Window valid. res=(6,0,5).
    • Try shrink: left=0('A'). If remove 'A', have becomes 2. Cannot shrink.
  3. right=6..9 ("ADOBECODEB"): No new chars from t fully satisfied.
  4. right=10 ("ADOBECODEBA"): `have` is 3. Window [0..10] valid.
    • Try shrink: left=0('A'). Can remove (another 'A' at index 10). left=1. Window [1..10] "DOBECODEBA". Still valid. ... Shrink until left=5. Window [5..10] "CODEBA" (len 6). No better than res.
  5. right=11 ("ADOBECODEBAN"): ...
  6. right=12 ("ADOBECODEBANC"): Window [5..12] "CODEBANC" valid.
    • Try shrink: ... until left=9. Window [9..12] "BANC" (len 4). res=(4,9,12).
Result: "BANC".
from collections import Counter
def minWindow(s: str, t: str) -> str:
    if not s or not t: return ""
    need = Counter(t)
    required = len(need)
    have = 0
    window_counts = Counter()
    res = (float("inf"), None, None)
    left = 0
    for right, ch in enumerate(s):
        window_counts[ch] += 1
        if ch in need and window_counts[ch] == need[ch]:
            have += 1
        while have == required:
            if right - left + 1 < res[0]:
                res = (right - left + 1, left, right)
            char_left = s[left]
            window_counts[char_left] -= 1
            if char_left in need and window_counts[char_left] < need[char_left]:
                have -= 1
            left += 1
    return s[res[1]:res[2]+1] if res[0] != float("inf") else ""

Interactive Animation (Container With Most Water)

The animation below demonstrates the "Container With Most Water" problem using the two-pointer technique. Input array: [1,8,6,2,5,4,8,3,7]. Watch how the pointers converge and the max area is updated.

L
R
Max Area: 0, Current Area: 0

Click 'Start Animation' to visualize Container With Most Water.

© Two Pointers Technique Explained. Content adapted for educational purposes.

We use cookies to improve your experience. By clicking “Accept” you consent to the use of cookies. Read our Privacy Policy.