Lesson 9 of 9 · 18m
Greedy and Two-Pointer Patterns
The patterns that solve a surprising number of problems — greedy choice, two pointers on sorted data, sliding windows — and when each applies.
Most interview-style problems aren't about knowing a specific algorithm — they're about recognizing a pattern. Three patterns cover a surprising share.
Two pointers on sorted data
def two_sum_sorted(nums, target):
l, r = 0, len(nums) - 1
while l < r:
s = nums[l] + nums[r]
if s == target: return l, r
if s < target: l += 1 # too small — need bigger left
else: r -= 1 # too big — need smaller rightOn sorted data, one pointer at each end converges in O(n) — no hash map needed. Same idea: palindrome check, "closest pair," container-with-most-water.
Sliding window
def longest_no_repeat(s):
seen, left, best = {}, 0, 0
for right, c in enumerate(s):
if c in seen and seen[c] >= left:
left = seen[c] + 1 # shrink window
seen[c] = right
best = max(best, right - left + 1)
return best"Longest substring with no repeats," "max sum of k consecutive," "min window substring" — all sliding window: expand right, shrink left when the invariant breaks.
Greedy: take the locally optimal
When a problem's optimal solution is built from locally-optimal choices, a greedy algorithm works — always take the best immediate move and never look back.
# activity selection: always pick the earliest-ending compatible activity
def max_activities(intervals):
intervals.sort(key=lambda x: x[1])
count, end = 0, 0
for s, e in intervals:
if s >= end:
count += 1
end = e
return countGreedy works when a problem has the "greedy choice property" — a local optimum leads to global optimum. When it doesn't (most interesting problems), DP or a smarter structure is the answer.
Field notes
- Recognize the pattern by the constraints: sorted input → two pointers; contiguous subarray → sliding window; "maximize count with non-overlap" → greedy.
- A greedy solution is fast and wrong if the problem doesn't have the property — test on the edge cases before trusting it.