Mastering Recursion: 10 Powerful Patterns Every Developer Should Know

Table of contents
- 1. Pick / Not Pick Pattern
- 2. Count All Ways (Total Paths)
- 3. Backtracking (Generate All)
- 4. Boolean Path Exists
- 5. Target Matching (Subset/K-Sum Style)
- 6. Recursion with Extra State (DP-ready)
- 7. Build & Recurse (Constructive Recursion)
- 8. Combinatorial Constraints
- 9. Min/Max Path Recursion
- 10. DFS-style Recursive Exploration

Recursion isnβt just some abstract idea β itβs like a toolbox filled with patterns. A lot of the problems that seem daunting in recursion can actually be solved once you identify the right pattern to use.
In this post, Iβm going to walk you through 10 distinct recursion techniques β each one featuring:
When to apply it
A handy trick to remember
The structure of the base case
A code template to guide you
The best LeetCode problems to practice with
1. Pick / Not Pick Pattern
When to Use: Choose or skip an element (e.g., sum, selection)
Trick:
pick = nums[i] + f(i+2)
,notPick = f(i+1)
Base Case: Index out of bounds
javaCopyEditint solve(int[] nums, int i) {
if (i >= nums.length) return 0;
int pick = nums[i] + solve(nums, i + 2);
int notPick = solve(nums, i + 1);
return Math.max(pick, notPick);
}
LeetCode Practice:
2. Count All Ways (Total Paths)
When to Use: Count total valid paths/ways
Trick:
return left + right
Base Case: Return
1
if path is valid,0
if invalid
javaCopyEditint countPaths(int i, int j) {
if (outOfBounds) return 0;
if (reachedEnd) return 1;
return countPaths(i + 1, j) + countPaths(i, j + 1);
}
π LeetCode Practice:
3. Backtracking (Generate All)
When to Use: Generate all combinations or permutations
Trick: Modify state β recurse β backtrack
Base Case: Add temp list to result
javaCopyEditvoid backtrack(List<Integer> temp, int[] nums, boolean[] used) {
if (temp.size() == nums.length) {
result.add(new ArrayList<>(temp));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i]) continue;
used[i] = true;
temp.add(nums[i]);
backtrack(temp, nums, used);
used[i] = false;
temp.remove(temp.size() - 1);
}
}
π LeetCode Practice:
4. Boolean Path Exists
When to Use: "Can I reach target?", "Is path possible?"
Trick: Return
true
if any path works (||
)Base Case: Return
true
if target met or path valid
javaCopyEditboolean canReach(int[] nums, int i, int target) {
if (i == nums.length) return target == 0;
return canReach(nums, i+1, target - nums[i]) || canReach(nums, i+1, target);
}
π LeetCode Practice:
5. Target Matching (Subset/K-Sum Style)
When to Use: Subset sum-style questions
Trick:
recurse(i+1, target - nums[i])
Base Case: If
target == 0 β true
javaCopyEditboolean solve(int i, int target) {
if (target == 0) return true;
if (i >= nums.length || target < 0) return false;
return solve(i+1, target - nums[i]) || solve(i+1, target);
}
π LeetCode Practice:
6. Recursion with Extra State (DP-ready)
When to Use: State changes beyond index (e.g., previous value, count)
Trick: Add extra param & memoize
Base Case: i out of bounds or limit reached
javaCopyEditint solve(int i, int prev) {
if (i == nums.length) return 0;
int take = 0;
if (nums[i] > prev) {
take = 1 + solve(i + 1, nums[i]);
}
int notTake = solve(i + 1, prev);
return Math.max(take, notTake);
}
π LeetCode Practice:
7. Build & Recurse (Constructive Recursion)
When to Use: Word break, expressions, palindromes
Trick: Build prefix β validate β recurse remainder
Base Case: If string fully processed
javaCopyEditvoid solve(String s, int idx, String path) {
if (idx == s.length()) {
result.add(path);
return;
}
for (int i = idx+1; i <= s.length(); i++) {
String part = s.substring(idx, i);
if (isValid(part)) {
solve(s, i, path + part + " ");
}
}
}
π LeetCode Practice:
8. Combinatorial Constraints
When to Use: Placing items with rules (N-Queens, Sudoku)
Trick: Loop over choices β check validity β recurse
Base Case: All placed
javaCopyEditfor (int col = 0; col < n; col++) {
if (isSafe(row, col)) {
placeQueen(row, col);
recurse(row + 1);
removeQueen(row, col);
}
}
π LeetCode Practice:
9. Min/Max Path Recursion
When to Use: Grid/path questions for min or max
Trick: Return
val + min(down, right)
Base Case: At bottom/right-most cell
javaCopyEditint solve(int i, int j) {
if (i == m-1 && j == n-1) return grid[i][j];
int down = solve(i+1, j);
int right = solve(i, j+1);
return grid[i][j] + Math.min(down, right);
}
π LeetCode Practice:
10. DFS-style Recursive Exploration
When to Use: Matrix or graph traversal (flood fill, island count)
Trick: Visit β Recurse on neighbors β Unvisit (if needed)
Base Case: Out of bounds or already visited
javaCopyEditvoid dfs(int i, int j, boolean[][] vis) {
if (invalid(i, j, vis)) return;
vis[i][j] = true;
for (int[] d : directions) {
dfs(i + d[0], j + d[1], vis);
}
}
π LeetCode Practice:
Letβs Connect!
If you found this helpful, share it with your peers.
Connect with me on LinkedIn for more resources, DSA guides, and community discussions.
Subscribe to my newsletter
Read articles from Bama Charan Chhandogi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Bama Charan Chhandogi
Bama Charan Chhandogi
I am a Dev from Kolkata who loves to write about technology.