Pattern 1: Two Pointers
Using two indices to traverse from different ends or at different speeds. Essential for strings and sorted arrays.
Reverse a String
What You'll Learn
The foundation of in-place modification.
Real-World Context
"Flipping a string without using extra memory."
Breaking It Down
- Place one pointer at the start (0) and one at the end (len-1)
- Swap the characters at these positions
- Move pointers toward each other
- Stop when they meet or cross
def reverse_string(s):
chars = list(s)
left, right = 0, len(chars) - 1
while left < right:
chars[left], chars[right] = chars[right], chars[left]
left += 1
right -= 1
return "".join(chars)Line-by-Line Walkthrough
By swapping from both ends, we only need to traverse half the string. This is O(N) time and O(1) extra space in C++.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Single pass, in-place swaps.
Common Mistakes
- ❌ Using extra space
✅ Use two pointers to swap in-place
Try It Yourself
Can you reverse it recursively?
Palindrome Check
What You'll Learn
Symmetry detection using pointers.
Real-World Context
"Checking if 'radar' or 'madam' reads the same backwards."
Breaking It Down
- Clean the string (lowercase, remove non-alphanumeric)
- Use two pointers at start and end
- If characters don't match, it's not a palindrome
- If pointers meet, it is a palindrome
def is_palindrome(s):
l, r = 0, len(s) - 1
while l < r:
if s[l].lower() != s[r].lower():
return False
l += 1
r -= 1
return TrueLine-by-Line Walkthrough
Similar to reversal, but we compare instead of swap. This pattern is used in 'Valid Palindrome II' and 'Longest Palindromic Substring'.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Linear scan with no extra storage.
Common Mistakes
- ❌ Not handling case sensitivity
✅ Convert to lowercase before comparing
Try It Yourself
Try 'Valid Palindrome II' where you can delete one character.
Pairs with Given Sum
What You'll Learn
Searching in a sorted space.
Real-World Context
"Find two numbers in a sorted array that add up to a target."
Breaking It Down
- Sort the array (if not already sorted)
- Start pointers at both ends
- If sum > target, move right pointer left (to decrease sum)
- If sum < target, move left pointer right (to increase sum)
def find_pairs(arr, target):
arr.sort()
l, r = 0, len(arr) - 1
while l < r:
curr_sum = arr[l] + arr[r]
if curr_sum == target:
return [arr[l], arr[r]]
elif curr_sum < target:
l += 1
else:
r -= 1
return []Line-by-Line Walkthrough
This 'Two-Sum II' pattern is much faster than nested loops (O(N) vs O(N²)) once the array is sorted.
Complexity Analysis
- Time: O(N log N) for sorting
- Space: O(1)
- Sorting dominates, then a linear scan.
Common Mistakes
- ❌ Forgetting to sort
✅ Two pointers only work on sorted arrays
Try It Yourself
What if the array isn't sorted? Use a HashSet.
Remove Duplicates
What You'll Learn
In-place array modification.
Real-World Context
"Remove duplicates from a sorted array without extra space."
Breaking It Down
- Use a 'slow' pointer for the unique elements
- Use a 'fast' pointer to scan the array
- If fast element != slow element, increment slow and update
def remove_duplicates(nums):
if not nums: return 0
slow = 0
for fast in range(1, len(nums)):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
return slow + 1Line-by-Line Walkthrough
This pattern is essential for 'Move Zeroes' and 'Remove Element'.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Single pass, in-place.
Common Mistakes
- ❌ Using a new array
✅ The problem usually requires O(1) space
Try It Yourself
Can you allow at most two duplicates?
Pattern 2: Hashing / Frequency Counting
Using HashMaps or Arrays to store and look up data in O(1) time.The most powerful tool for 'existence' problems.
Two Sum (Unsorted)
What You'll Learn
The classic HashMap lookup.
Real-World Context
"Find two numbers that add to target in an unsorted array."
Breaking It Down
- Create an empty HashMap (value -> index)
- For each number, calculate the complement (target - num)
- If complement exists in map, return the pair
- Otherwise, add current number to map
def two_sum(nums, target):
seen = {}
for i, num in enumerate(nums):
diff = target - num
if diff in seen:
return [seen[diff], i]
seen[num] = i
return []Line-by-Line Walkthrough
This is the O(N) version of Two Sum. It trades space for time.
Complexity Analysis
- Time: O(N)
- Space: O(N)
- We store up to N elements in the map.
Common Mistakes
- ❌ Checking for the same element twice
✅ Add the element to the map AFTER checking the complement
Try It Yourself
What if there are multiple pairs?
Anagram Check
What You'll Learn
Comparing content regardless of order.
Real-World Context
"Are 'listen' and 'silent' made of the same letters?"
Breaking It Down
- Check if lengths are equal
- Count frequency of each character in string A
- Subtract frequency for each character in string B
- If all counts are zero, they are anagrams
from collections import Counter
def are_anagrams(s1, s2):
return Counter(s1) == Counter(s2)Line-by-Line Walkthrough
Instead of sorting (O(N log N)), hashing gives us O(N). This pattern solves 'Group Anagrams' and 'Find All Anagrams in a String'.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Fixed size array (256) for ASCII.
Common Mistakes
- ❌ Sorting both strings
✅ Sorting is O(N log N), hashing is O(N)
Try It Yourself
How would you handle Unicode characters instead of just ASCII?
First Non-Repeating Character
What You'll Learn
Two-pass hashing.
Real-World Context
"Find the first character that appears only once in 'swiss' (it's 'w')."
Breaking It Down
- Pass 1: Build a frequency map of all characters
- Pass 2: Iterate through the string and return the first char with count 1
def first_unique(s):
counts = {}
for c in s: counts[c] = counts.get(c, 0) + 1
for c in s:
if counts[c] == 1: return c
return NoneLine-by-Line Walkthrough
The first pass 'remembers' the counts, the second pass 'finds' the result. Essential for 'Top K Frequent Elements'.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Max 256 unique characters.
Common Mistakes
- ❌ Only doing one pass
✅ You need the first pass to know the total counts
Try It Yourself
Can you do this with a single pass using a LinkedHashMap?
Pattern 3: Slow & Fast Pointers
Also known as the Tortoise and Hare algorithm. Used primarily for Linked Lists and cycle detection.
Detect Loop in Linked List
What You'll Learn
Floyd's Cycle-Finding Algorithm.
Real-World Context
"Does the linked list go on forever in a circle?"
Breaking It Down
- Initialize 'slow' and 'fast' pointers at the head
- Move slow by 1 step, fast by 2 steps
- If they ever meet, there is a loop
- If fast hits NULL, there is no loop
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast: return True
return FalseLine-by-Line Walkthrough
In a circular track, the faster runner will eventually lap the slower one. This is O(N) time and O(1) space.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Linear traversal with two pointers.
Common Mistakes
- ❌ Not checking fast.next
✅ Always check both fast and fast.next to avoid null pointer errors
Try It Yourself
Can you find the exact node where the cycle starts?
Find Middle of Linked List
What You'll Learn
Single-pass middle finding.
Real-World Context
"Find the center node without knowing the total length."
Breaking It Down
- Move slow by 1, fast by 2
- When fast reaches the end, slow is exactly at the middle
def find_middle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slowLine-by-Line Walkthrough
This avoids the 'two-pass' approach (count then find). Used in 'Reorder List' and 'Palindrome Linked List'.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Traverses the list once.
Common Mistakes
- ❌ Off-by-one errors
✅ Carefully trace if you need the first or second middle for even lengths
Try It Yourself
How would you find the 1/3rd point of the list?
Pattern 4: Single-Pass Traversal
Solving problems by visiting each element exactly once and maintaining state in variables.
Find Maximum Element
What You'll Learn
The 'King of the Hill' logic.
Real-World Context
"Finding the largest number in an unsorted array."
Breaking It Down
- Assume the first element is the maximum
- Compare it with every other element
- Update maximum if a larger one is found
def find_max(arr):
max_val = arr[0]
for x in arr:
if x > max_val: max_val = x
return max_valLine-by-Line Walkthrough
The simplest but most fundamental pattern. Used in 'Kadane's Algorithm' and 'Best Time to Buy and Sell Stock'.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- One loop, one variable.
Common Mistakes
- ❌ Initializing max to 0
✅ Use the first element or negative infinity to handle negative numbers
Try It Yourself
Find the minimum and maximum in a single pass with fewer comparisons.
Second Largest Element
What You'll Learn
Tracking multiple states simultaneously.
Real-World Context
"Find the runner-up without sorting."
Breaking It Down
- Maintain 'largest' and 'second_largest'
- If current > largest: update both
- Else if current > second_largest: update only second
def second_largest(arr):
first = second = float('-inf')
for x in arr:
if x > first:
second = first
first = x
elif x > second and x != first:
second = x
return secondLine-by-Line Walkthrough
Teaches you how to maintain multiple 'best' values. Essential for 'Kth Largest Element' problems.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Single pass, constant variables.
Common Mistakes
- ❌ Not handling duplicates
✅ Ensure second_largest is only updated if current != largest
Try It Yourself
Find the Kth largest element using a Min-Heap.
Pattern 5: In-Place Swapping
Modifying the input data structure directly to save memory. Crucial for system-level programming.
Reverse an Array
What You'll Learn
Memory-efficient data manipulation.
Real-World Context
"Flip an array [1,2,3] to [3,2,1] without creating a new one."
Breaking It Down
- Use two pointers (start, end)
- Swap elements and move pointers
- Stop at the middle
def reverse_array(arr):
l, r = 0, len(arr) - 1
while l < r:
arr[l], arr[r] = arr[r], arr[l]
l += 1; r -= 1Line-by-Line Walkthrough
This is the array version of string reversal. It's the basis for 'Rotate Array' and 'Reverse Words in a String'.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- No extra memory used.
Common Mistakes
- ❌ Swapping until the end
✅ If you swap until the end, you'll just reverse it back to original
Try It Yourself
Rotate an array by K positions using three reversals.
Dutch National Flag (Sort 0s, 1s, 2s)
What You'll Learn
Three-pointer partitioning.
Real-World Context
"Sort an array of only 0s, 1s, and 2s in one pass."
Breaking It Down
- Use three pointers: low, mid, high
- If mid is 0: swap with low, increment low and mid
- If mid is 1: just increment mid
- If mid is 2: swap with high, decrement high
def sort_colors(arr):
low, mid, high = 0, 0, len(arr) - 1
while mid <= high:
if arr[mid] == 0:
arr[low], arr[mid] = arr[mid], arr[low]
low += 1; mid += 1
elif arr[mid] == 1:
mid += 1
else:
arr[mid], arr[high] = arr[high], arr[mid]
high -= 1Line-by-Line Walkthrough
This is a masterclass in pointer management. It's the core logic behind 'QuickSort Partitioning'.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Single pass, three pointers.
Common Mistakes
- ❌ Incrementing mid after swapping with high
✅ The new element from high hasn't been checked yet, so don't increment mid
Try It Yourself
Sort an array with 4 distinct values.
Pattern 6: Mathematical Optimization
Using mathematical properties to avoid unnecessary work. Turns O(N) into O(√N) or O(log N).
Check if Prime
What You'll Learn
The Square Root optimization.
Real-World Context
"Is 1000003 a prime number?"
Breaking It Down
- If n <= 1, return False
- Loop from 2 up to √n
- If any number divides n, it's not prime
import math
def is_prime(n):
if n <= 1: return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0: return False
return TrueLine-by-Line Walkthrough
If a number has a factor larger than its square root, it must also have one smaller than it. This saves millions of iterations for large numbers.
Complexity Analysis
- Time: O(√N)
- Space: O(1)
- Loop runs only up to square root.
Common Mistakes
- ❌ Looping up to N
✅ Looping to √N is significantly faster for large primes
Try It Yourself
Implement the Sieve of Eratosthenes to find all primes up to N.
Find Missing Number (1 to N)
What You'll Learn
The Summation Formula / XOR trick.
Real-World Context
"An array has numbers 1 to N, but one is missing. Find it."
Breaking It Down
- Calculate expected sum: N * (N + 1) / 2
- Calculate actual sum of array
- Missing = Expected - Actual
def missing_number(arr, n):
expected = n * (n + 1) // 2
return expected - sum(arr)Line-by-Line Walkthrough
This is O(N) time and O(1) space. The XOR version is even better as it prevents integer overflow.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Single pass, simple math.
Common Mistakes
- ❌ Integer overflow
✅ Use XOR or long long for very large N
Try It Yourself
Find two missing numbers in the same range.
GCD (Euclidean Algorithm)
What You'll Learn
Recursive reduction.
Real-World Context
"Find the Greatest Common Divisor of 48 and 18."
Breaking It Down
- GCD(a, b) is the same as GCD(b, a % b)
- Repeat until b becomes 0
- The remaining 'a' is the GCD
def gcd(a, b):
while b:
a, b = b, a % b
return aLine-by-Line Walkthrough
One of the oldest and most efficient algorithms. Used in 'Fraction Simplification' and 'RSA Encryption'.
Complexity Analysis
- Time: O(log(min(a,b)))
- Space: O(1)
- Numbers decrease exponentially.
Common Mistakes
- ❌ Not handling zero
✅ Ensure you don't divide by zero if b is 0
Try It Yourself
Find the LCM (Least Common Multiple) using the GCD.
Pattern 7: Recursion Thinking
Breaking a problem into smaller sub-problems. The foundation for Trees, Graphs, and Dynamic Programming.
Factorial (Iterative & Recursive)
What You'll Learn
Base cases and recursive calls.
Real-World Context
"Calculate 5! (5 * 4 * 3 * 2 * 1)."
Breaking It Down
- Base case: if n is 0 or 1, return 1
- Recursive step: return n * factorial(n-1)
def factorial(n):
if n <= 1: return 1
return n * factorial(n - 1)Line-by-Line Walkthrough
Teaches you how to trust the 'recursive leap of faith'. Essential for 'Tree Traversals' (DFS).
Complexity Analysis
- Time: O(N)
- Space: O(N)
- Recursive stack depth is N.
Common Mistakes
- ❌ Stack overflow
✅ For very large N, recursion will crash; use iteration
Try It Yourself
Calculate the number of trailing zeros in N!.
Fibonacci Sequence
What You'll Learn
State dependency.
Real-World Context
"0, 1, 1, 2, 3, 5, 8... each number is the sum of the two before it."
Breaking It Down
- Start with 0 and 1
- Next = Prev1 + Prev2
- Update Prev1 and Prev2
def fib(n):
a, b = 0, 1
for _ in range(n):
print(a)
a, b = b, a + bLine-by-Line Walkthrough
While recursion is intuitive here, iteration is much more efficient (O(N) vs O(2^N)). This is the gateway to 'Dynamic Programming'.
Complexity Analysis
- Time: O(N)
- Space: O(1)
- Iterative approach uses constant space.
Common Mistakes
- ❌ Naive recursion
✅ fib(n-1) + fib(n-2) without memoization is O(2^N)
Try It Yourself
Solve this using Dynamic Programming (Memoization).
Pattern 8: Stack / LIFO Pattern
Last-In, First-Out. Used for problems involving nested structures or 'undo' operations.
Balanced Parentheses
What You'll Learn
Matching nested structures.
Real-World Context
"Is '[{()}]' valid? Is '[(])' valid?"
Breaking It Down
- Use a stack to store opening brackets
- When you see a closing bracket, check if it matches the top of the stack
- If it matches, pop the stack. If not, it's invalid
- At the end, the stack must be empty
def is_valid(s):
stack = []
mapping = {")": "(", "}": "{", "]": "["}
for char in s:
if char in mapping:
top = stack.pop() if stack else '#'
if mapping[char] != top: return False
else:
stack.append(char)
return not stackLine-by-Line Walkthrough
The stack 'remembers' the order of opening brackets. This pattern is used in 'Min Stack', 'Evaluate Reverse Polish Notation', and 'Daily Temperatures'.
Complexity Analysis
- Time: O(N)
- Space: O(N)
- We might store all characters in the stack.
Common Mistakes
- ❌ Not checking if stack is empty before popping
✅ A closing bracket with no opening bracket is invalid
Try It Yourself
Handle multiple types of brackets like [], {}, and ().
Bonus: Searching & Sorting
The algorithms that power the world.
Binary Search
What You'll Learn
Divide and Conquer.
Real-World Context
"Find a number in a sorted array in O(log N) time."
Breaking It Down
- Find the middle element
- If target == mid, return index
- If target < mid, search the left half
- If target > mid, search the right half
def binary_search(arr, target):
l, r = 0, len(arr) - 1
while l <= r:
mid = (l + r) // 2
if arr[mid] == target: return mid
if arr[mid] < target: l = mid + 1
else: r = mid - 1
return -1Line-by-Line Walkthrough
Binary search is the most important algorithm to understand. It's the basis for 'Search in Rotated Sorted Array' and 'Find Peak Element'.
Complexity Analysis
- Time: O(log N)
- Space: O(1)
- Search space halves each step.
Common Mistakes
- ❌ Midpoint overflow
✅ Use low + (high - low) / 2 instead of (low + high) / 2
Try It Yourself
Find the first occurrence of a duplicate element.