DEV Community

Dev Cookies
Dev Cookies

Posted on

Blog 1: Backtracking Fundamentals – The Foundation of Recursive Problem Solving

Backtracking is one of the most powerful paradigms in DSA. It is widely used in interview questions (FAANG, MAANG, Tier-1 product companies) and competitive programming to solve problems involving searching, decision-making, and constraints.

In this blog, we’ll go step by step — intuition → template → example → variations.


🔹 What is Backtracking?

At its core, backtracking is brute force + pruning.

  • We try out possible choices (explore).
  • If the choice leads to an invalid solution → undo (backtrack).
  • If the choice works → continue exploring deeper.

👉 Think of it as a DFS search on a decision tree.


🔹 Key Components of Backtracking

Every backtracking solution revolves around three parts:

  1. Choices – What options do we have at this step?
  2. Constraints – Which choices are valid? (Pruning)
  3. Goal – When do we stop recursion?

🔹 Universal Backtracking Template (Java)

void backtrack(State state, List<Result> results) { if (goalReached(state)) { results.add(new Result(state)); return; } for (Choice choice : validChoices(state)) { // 1. Make a choice state.make(choice); // 2. Explore further backtrack(state, results); // 3. Undo the choice (backtrack) state.undo(choice); } } 
Enter fullscreen mode Exit fullscreen mode

✨ This template is reusable for subsets, permutations, combinations, pathfinding, puzzles, CSP problems.


🔹 Example: N-Queens (Classic Backtracking)

📌 Problem: Place N queens on an N×N chessboard such that no two queens attack each other.


Step 1: Choices

  • Place a queen in any column of the current row.

Step 2: Constraints

  • No two queens should share the same column, diagonal, or anti-diagonal.

Step 3: Goal

  • When all rows are filled → valid solution.

Java Code (N-Queens Solver)

import java.util.*; public class NQueens { public List<List<String>> solveNQueens(int n) { List<List<String>> results = new ArrayList<>(); char[][] board = new char[n][n]; for (char[] row : board) Arrays.fill(row, '.'); backtrack(0, board, results, n); return results; } private void backtrack(int row, char[][] board, List<List<String>> results, int n) { if (row == n) { results.add(construct(board)); return; } for (int col = 0; col < n; col++) { if (isSafe(board, row, col, n)) { // make choice board[row][col] = 'Q'; // explore backtrack(row + 1, board, results, n); // undo choice (backtrack) board[row][col] = '.'; } } } private boolean isSafe(char[][] board, int row, int col, int n) { // check column for (int i = 0; i < row; i++) if (board[i][col] == 'Q') return false; // check upper-left diagonal for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) if (board[i][j] == 'Q') return false; // check upper-right diagonal for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) if (board[i][j] == 'Q') return false; return true; } private List<String> construct(char[][] board) { List<String> res = new ArrayList<>(); for (char[] row : board) res.add(new String(row)); return res; } } 
Enter fullscreen mode Exit fullscreen mode

🔹 Recursion Tree Visualization

For N=4:

  • Row 0 → Try placing queen at col 0..3
  • Row 1 → Recurse only where previous placement is safe
  • Row 2 → Same logic
  • Row 3 → If reached → add solution

The recursion explores possibilities, prunes invalid ones, and backtracks to try other paths.


🔹 Complexity Analysis

  • Worst-case: O(N!) (factorial explosion in permutations).
  • With pruning: significantly reduced search space.
  • Space: O(N²) for board, O(N) recursion depth.

🔹 Key Takeaways

✅ Backtracking = DFS + Undo
✅ Always define Choice, Constraint, Goal
✅ Use the universal template for different problem types
✅ Pruning makes brute force efficient enough for interviews


🔹 What’s Next?

In Blog 2: Subset & Power Set Pattern, we’ll explore how backtracking generates subsets of a set, power sets, and their interview variations.

Top comments (0)