Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome to the first lesson in our new module on backtracking. In the previous module, we explored how heaps can efficiently manage and retrieve data based on priority. We analyzed the time and space complexity of heap-based solutions for problems like finding medians in a stream and optimizing project selection. Now, we shift our focus from a specific data structure to a powerful, general problem-solving strategy.
Today, our goal is to explain the backtracking framework in terms of choices, constraints, and goals. This technique is the key to solving a wide range of problems that involve searching, enumeration, and finding all possible solutions, such as generating subsets, permutations, or solving puzzles like Sudoku. By the end of this lesson, you will understand the conceptual blueprint that underlies all backtracking algorithms, preparing you to implement them in our upcoming lessons.
The Core Idea: A Systematic Search
At its heart, backtracking is a methodical way to explore all potential solutions to a problem. It's often described as a refined form of brute force. While a naive brute-force approach might generate every single possibility and then check if it's valid, backtracking is smarter. It builds a solution incrementally, and as soon as it determines that a particular path cannot possibly lead to a valid solution, it abandons that path and "backtracks" to try another.
The classic analogy is navigating a maze. You explore one path until you either reach the exit or hit a dead end. If you hit a dead end, you retrace your steps to the last junction and try a different path.
Solve ANY Backtracking Problem on Leetcode (Template + Explanation)
The video "Solve ANY Backtracking Problem on Leetcode" offers a great introduction to this core idea.
Watch the opening section from the beginning, which defines backtracking using the familiar maze analogy.
This process of exploring paths, hitting dead ends, and backtracking naturally forms a tree structure. We can visualize every backtracking problem as a decision tree or state-space tree.

You can think of backtracking as a Depth-First Search (DFS) on this implicit decision tree. You've already implemented iterative DFS for binary trees; backtracking applies the same "go deep, then backtrack" logic to a more abstract space of choices and states.
The Three Pillars of Backtracking
To apply backtracking, you must first frame the problem in terms of three fundamental components. Identifying these is the most crucial step in solving any backtracking problem.
The blog post "Backtracking" by Harisai clearly outlines these three components.
Read the section "The Three Pillars of Backtracking". It concisely defines the core concepts of Choice, Constraints, and Goal, which we will now explore in more detail.
Let's break down these three pillars:
-
Choice: At any given point in building a solution, what are the next steps you can take? These are the branches extending from your current node in the decision tree. For a Sudoku puzzle, the choice at an empty cell is which number (1-9) to place there. For a maze, the choice at a junction is which path to take.
-
Constraints: What rules must a choice obey to be considered valid? Constraints are what make backtracking more efficient than brute force. By checking constraints early, we can "prune" entire branches of the decision tree that we know will not lead to a valid solution. In Sudoku, a constraint is that a number cannot already exist in the same row, column, or 3x3 subgrid.
-
Goal: How do you know when you have found a complete, valid solution? This is your base case in the recursion. It's the condition that tells you to stop exploring a particular path and record your success. In Sudoku, the goal is reached when the entire board is filled according to the rules.
This video provides an excellent walkthrough of applying these three keys to the Sudoku problem, making the abstract concepts concrete.
The Backtracking Blueprint: The Legendary 3 Keys To Backtracking Algorithms
The video "The Backtracking Blueprint" by Back To Back SWE demonstrates how to deconstruct a problem using these three keys.
Watch the following segments to see how the framework applies to solving Sudoku: The introduction to the three keys: choices, constraints, and goal. The discussion on choice, identifying the decision space (placing numbers 1-9 in a cell). The explanation of constraints, which involves validating the placement against row, column, and subgrid rules. The definition of the goal, which becomes the base case for the recursion (e.g., reaching the end of the board).
The Mechanics: Attempt, Recurse, and Undo
With the "what" (choices, constraints, goal) defined, let's look at the "how." The backtracking algorithm is implemented recursively, following a simple but powerful pattern:
- Make a Choice (Attempt): Choose one of the available options.
- Explore (Recurse): Call the function recursively to continue building the solution from this new state.
- Undo the Choice (Backtrack): After the recursive call returns (meaning it has fully explored that path), reverse the choice you made. This is the critical step that allows you to return to the previous state and try the next available option.
This "choose, explore, un-choose" cycle is the engine of backtracking. The "un-choose" step is what cleans the slate, ensuring that the decision made in one recursive path doesn't incorrectly affect a different one.
The "Hello Algo" article provides a clear illustration of this mechanical process.
13.1 Backtracking algorithms - Hello Algo
This resource walks through the mechanics of making and undoing choices within a recursive function.
First, read the section on "Attempt and Backtrack". This section uses a tree path-finding problem to show how adding an element to a path (path.push()) is the "attempt," and removing it (path.pop()) is the "backtrack" or "undo" step. Next, read the section on "Pruning". This connects the idea of constraints to the practical action of pruning, where you avoid making a recursive call if a choice is invalid, thereby saving significant computation.
The General Backtracking Framework
By combining these concepts, we can create a generic template that serves as a blueprint for almost any backtracking problem. While you won't be implementing this just yet, understanding its structure is key.
function backtrack(current_state, other_params...) {
// 1. Goal Check (Base Case)
if (is_solution(current_state)) {
add solution to results;
return; // or continue if multiple solutions are needed
}
// 2. Iterate through Choices
for (choice in available_choices) {
// 3. Apply Constraints (Pruning)
if (is_valid(choice)) {
// 4a. Make Choice (Attempt)
make_choice(choice);
// 4b. Explore (Recurse)
backtrack(new_state, ...);
// 4c. Undo Choice (Backtrack)
undo_choice(choice);
}
}
}
This structure neatly maps to our three pillars: the for loop handles the Choices, the if (is_valid) check applies the Constraints, and the if (is_solution) check identifies the Goal. The body of the loop contains the "attempt, recurse, undo" mechanical cycle.
Conclusion
In this lesson, you've been introduced to the conceptual framework of backtracking. We've moved beyond a simple definition to see it as a structured form of DFS on a state-space tree, guided by three pillars. Recognizing these components in a problem is the first and most important skill in mastering this powerful technique.
Key Takeaways:
- Backtracking is a recursive, systematic search algorithm that builds solutions incrementally and abandons paths that violate constraints.
- It can be visualized as a Depth-First Search (DFS) on an implicit decision tree (or state-space tree).
- Every backtracking problem can be broken down into three components:
- Choices: The set of possible moves or additions to the current partial solution.
- Constraints: The rules that a choice must satisfy to be valid, which allow for "pruning" the search space.
- Goal: The condition that defines a complete and valid solution, which serves as the recursive base case.
- The core mechanism follows an "attempt, recurse, undo" pattern: make a choice, explore its consequences, and then reverse the choice to explore other possibilities.
This conceptual foundation is crucial. In our next lesson, we will put this theory into practice by writing our first backtracking algorithm to generate all subsets of a set. You will see exactly how this template translates into working code.