Beginner: foundations · Hashing, two pointers and sliding window · topic 13 of 61 · 40 min
Two pointers
Explanation
Why two pointers
A pointer here is just an index that marks a position. Two-pointer techniques move two indexes through an array so that each step rules out a whole group of answers. They often turn an O(n²) "try every pair" into one O(n) pass with O(1) extra space. There are two main shapes: pointers at opposite ends moving towards each other, and two pointers moving in the same direction at different speeds.
Opposite ends: a pair with a target sum
In a SORTED array, put left at the start and right at the end. If nums[left] + nums[right] is too small, the only way to grow the sum is to move left forward. If it is too big, move right back. If it equals the target, you are done.
Why no pair is missed: when the sum is too small, nums[left] plus the LARGEST value still available is too small, so nums[left] cannot be part of any answer and can be dropped. The same argument drops nums[right] when the sum is too big. Every step drops one item, so there are at most n steps: O(n) time and O(1) space. The hash map from Topic 1 is also O(n) time, but it needs O(n) space; two pointers need sorted input.
def pair_sum(nums, target):
left, right = 0, len(nums) - 1
while left < right:
s = nums[left] + nums[right]
if s == target:
return [left, right]
if s < target:
left += 1
else:
right -= 1
return []
print(pair_sum([1, 3, 4, 6, 9], 10)) # Output: [0, 4]
print(pair_sum([1, 3, 4, 6, 9], 8)) # Output: []Container with most water
heights[i] is the height of a vertical line at position i. Two lines and the ground form a container that holds min(heights[l], heights[r]) × (r − l) units of water. Start with the widest container (l = 0, r = n − 1), record its area, then move the pointer at the SHORTER line inwards. Moving the taller one can never help: the width shrinks and the height is still capped by the same shorter line. O(n) time, O(1) space.
For heights [1, 8, 6, 2, 5, 4, 8, 3, 7] the best container uses the 8 at index 1 and the 7 at index 8: min(8, 7) × (8 − 1) = 49.
Same direction: slow and fast pointers
Here both pointers move left to right. The fast (read) pointer looks at every item; the slow (write) pointer marks where the next kept item goes. This is the write-pointer pattern from Module 2, and it filters an array in place in O(n) time and O(1) space. Problems usually ask you to return k, the number of kept items, and promise to look only at nums[:k].
def remove_value(nums, val):
write = 0
for read in range(len(nums)):
if nums[read] != val:
nums[write] = nums[read]
write += 1
return write # nums[:write] holds the kept items
a = [3, 2, 2, 3, 4]
k = remove_value(a, 3)
print(k, a[:k]) # Output: 3 [2, 2, 4]To remove duplicates from a SORTED array, only the test changes: equal values sit next to each other, so keep nums[read] when it differs from the last kept item, nums[write − 1].
Merging two sorted arrays
Put one pointer at the start of each sorted array. Repeatedly take the smaller of the two front items and move that pointer on. When one array runs out, copy the rest of the other. Each step moves one pointer, so merging arrays of sizes n and m is O(n + m). This merge is the heart of merge sort (Module 6) and of merging sorted linked lists (Module 4).
def merge(a, b):
i = j = 0
out = []
while i < len(a) and j < len(b):
if a[i] <= b[j]:
out.append(a[i])
i += 1
else:
out.append(b[j])
j += 1
out.extend(a[i:])
out.extend(b[j:])
return out
print(merge([1, 4, 9], [2, 3, 10, 12])) # Output: [1, 2, 3, 4, 9, 10, 12]3-sum: sort, fix one, two-pointer the rest
To find all unique triplets that sum to 0, sort first (O(n log n)). Then, for each index i, look for pairs to the right of i that sum to −nums[i] with the opposite-ends walk. That is O(n) for each i, so O(n²) in total, much better than O(n³) for checking all triples.
Duplicates are the tricky part. Skip i when nums[i] == nums[i − 1], because the same first value would give the same triplets again. After finding a triplet, move left past every copy of its value and right past every copy of its value before continuing. You can also stop early when nums[i] > 0: every later number is at least as big, so no sum can reach 0.
Common mistakes
- Using opposite-end pointers on an UNSORTED array. Fix: sort first (and remember that sorting changes the indexes), or use a hash map.
- Writing while left <= right in pair sum, which can pair an item with itself. Fix: while left < right.
- Moving the taller line in container with most water. Fix: always move the shorter one.
- Forgetting to skip duplicates in 3-sum, so the same triplet appears twice. Fix: skip equal values for i, and after each match move past equal values on both sides.
- Returning the list instead of the new length k when filtering in place, or reading items beyond k. Fix: return k and use only nums[:k].
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Two pointers · 1/8
This lesson is about two pointers: two indexes that move in a planned way so that each step rules out many answers.
Prefer a video by a teacher? Find free videos on “two pointers technique container with most water 3sum remove duplicates sorted array” on YouTube ↗
In real life: Two products for a gift card
A shopping site can suggest "two items that use up your Rs 1,000 gift card exactly". Product prices are often already sorted for the price filter, so the two-pointer walk finds a matching pair in one pass with no extra memory: too cheap, try a pricier first item; too expensive, try a cheaper second item.
prices = [149, 299, 350, 499, 650, 850]
left, right = 0, len(prices) - 1
while left < right:
total = prices[left] + prices[right]
if total == 1000:
print(prices[left], prices[right])
break
if total < 1000:
left += 1
else:
right -= 1Practice (0/4 done)
nums is sorted in non-decreasing order. Write pair_sum_sorted(nums, target) that returns [i, j] with i < j and nums[i] + nums[j] == target, using two pointers from opposite ends (O(n) time, O(1) extra space). If several pairs work, return the one with the smallest i, and for that i the largest j (this is exactly what the standard walk finds). If none, return []. Example: pair_sum_sorted([2, 7, 11, 15], 9) returns [0, 1].
nums is sorted in non-decreasing order. Write remove_duplicates(nums) that removes duplicates IN PLACE so the first k items are the distinct values in their original order, and returns k. What is left after position k does not matter. O(n) time, O(1) extra space; do not create a new list. Example: for [1, 1, 2] return 2, and nums[:2] is [1, 2].
heights[i] is the height of a vertical line at position i. Write max_area(heights) that returns the most water a container formed by two lines can hold: min(heights[l], heights[r]) * (r - l). Use two pointers in O(n) time. With fewer than 2 lines, return 0. Example: max_area([1, 8, 6, 2, 5, 4, 8, 3, 7]) returns 49.
Write three_sum(nums) that returns every UNIQUE triplet [a, b, c] of values at three different positions with a + b + c == 0. Write each triplet in increasing order (a <= b <= c) and return the list of triplets sorted. Sort nums and use two pointers: O(n²) time. Example: three_sum([-1, 0, 1, 2, -1, -4]) returns [[-1, -1, 2], [-1, 0, 1]].
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Sliding window: fixed sizeHashing, two pointers and sliding window · same module
- Hash maps and hash setsHashing, two pointers and sliding window · same module
- Fast and slow pointers and merging listsStacks, queues and linked lists
- Data-structure questions in JavaScriptJavaScript Programming (language in depth) · Interview preparation and career
- Lists: many values in one namePython Programming · Lists, tuples, dictionaries and sets
- Pointers and arraysC Programming (from zero to systems) · Pointers
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
nums is sorted and nums[left] + nums[right] is LESS than the target. What should you do?
Answer: Move left one step right
Only a bigger left value can raise the sum; nums[left] cannot pair with anything left in the range.
In container with most water, heights[left] = 3 and heights[right] = 8. Which pointer moves?
Answer: left, because it is the shorter line
Every container that keeps the 3 is narrower and still capped at height 3, so the 3 can be dropped.
What is the time of 3-sum with sorting and two pointers?
Answer: O(n²)
Sorting is O(n log n), then an O(n) walk for each of n values of i gives O(n²).
Why can the opposite-ends walk drop nums[left] when the sum is too small?
Answer: Because even with the largest value still in range, nums[right], the sum is too small
Every other partner for nums[left] is smaller than nums[right], so every pair with nums[left] is too small.
Merging sorted arrays of sizes n and m with two pointers costs…
Answer: O(n + m)
Each step moves one pointer forward, so there are n + m steps in total.
n items and q range-sum queries. What is the total cost with a prefix array?Related
Why is reading nums[i] O(1) in an array?Related
Answer: Its position is start + i × size, one calculation
Items sit side by side and have the same size, so the address of item i is computed directly.
When merging two sorted lists, one list runs out. What do you do with the other?Related
Answer: Attach its remaining part in one step: tail.next = a or b
The remaining part is already sorted and larger than everything merged so far.
Which pattern fits "find two numbers that add up to a target" in unsorted data?Related
Answer: Hash map
Store what you have seen and look up the complement.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Hashing, two pointers and sliding window” before its exam: Sliding window: fixed size, Sliding window: variable size.
- Module 3 exam: Hashing, two pointers and sliding window · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Stacks, queues and linked lists. 61 topics left in the course before the final exam.