This document explains the Depth-First Search (DFS) and backtracking algorithmic pattern as implemented in the repository's problem solutions. DFS is a fundamental graph/tree traversal technique, while backtracking extends DFS to explore solution spaces by building candidates incrementally and abandoning them when they cannot lead to valid solutions. This page covers recursive tree traversal, constraint satisfaction problems, and combinatorial search implementations.
For array manipulation techniques, see Two Pointers Pattern. For optimization problems with overlapping subproblems, see Dynamic Programming Pattern. For exhaustive state space exploration without pruning, see standard DFS implementations in tree/graph problems.
DFS and backtracking share a common recursive structure but differ in their goals:
Both patterns rely on:
Key characteristics in this repository:
solution/, lcci/, lcof/, and lcp/ directoriesSources: Overall repository structure from provided diagrams
Sources: General DFS pattern
The repository implements tree DFS traversal in problems like the Lowest Common Ancestor (LCA) problem, which demonstrates post-order DFS with result propagation.
Implementation in Python:
lcci/04.08.First Common Ancestor/Solution.py10-17
Implementation in Java:
lcci/04.08.First Common Ancestor/Solution.java10-18
Implementation in C++:
lcci/04.08.First Common Ancestor/Solution.cpp10-20
Key DFS characteristics demonstrated:
root is None/null or matches target nodesThe time complexity is $O(n)$ where $n$ is the number of tree nodes, as each node is visited exactly once. Space complexity is $O(h)$ for the recursion stack, where $h$ is the tree height.
Sources: lcci/04.08.First Common Ancestor/README.md70-79 lcci/04.08.First Common Ancestor/README_EN.md70-79 lcci/04.08.First Common Ancestor/Solution.py1-18 lcci/04.08.First Common Ancestor/Solution.java1-19 lcci/04.08.First Common Ancestor/Solution.cpp1-21
Backtracking problems follow a standard template that explores the solution space systematically:
Generic backtracking pseudocode structure:
Sources: Standard backtracking pattern
| Component | Purpose | Example |
|---|---|---|
| Current Path | Tracks choices made so far | List of queens' positions, string partition indices |
| State | Represents problem constraints | Board configuration, used characters set |
| Choices | Available options at current step | Available board positions, remaining characters |
| Constraint Check | Validates whether choice is legal | No two queens attack each other, palindrome validation |
| State Restoration | Undoes choice when backtracking | Remove queen from board, remove from used set |
Sources: General backtracking concepts
The classic N-Queens problem places N queens on an N×N chessboard such that no two queens attack each other. This demonstrates pure backtracking with constraint checking.
Problem structure:
Backtracking approach:
Typical implementation structure:
Complexity:
Sources: Standard N-Queens backtracking pattern
String partitioning problems use backtracking to split strings into valid substrings, with constraint checking at each partition point.
Problem structure:
Decision tree example for "aab":
Implementation pattern:
Sources: Standard palindrome partition backtracking pattern
Backtracking excels at generating combinations, permutations, and subsets:
| Problem Type | State | Choices | Constraint |
|---|---|---|---|
| Combinations | Current combination, start index | Remaining elements | No duplicates, size limit |
| Permutations | Current permutation, used set | Unused elements | Each element used once |
| Subsets | Current subset, index | Include or skip element | Size constraints if any |
| Combination Sum | Current sum, combination | Candidates (possibly reusable) | Sum equals target |
Common pattern for combinations:
Sources: Standard combinatorial backtracking patterns
The repository provides multi-language implementations following consistent patterns. Here's how key components map across languages:
Recursion and base cases:
| Language | Base Case Check | Recursion | Example Reference |
|---|---|---|---|
| Python | if root is None or root in [p, q]: | left = self.lowestCommonAncestor(root.left, p, q) | lcci/04.08.First Common Ancestor/Solution.py10-14 |
| Java | if (root == null || root == p || root == q) | TreeNode left = lowestCommonAncestor(root.left, p, q); | lcci/04.08.First Common Ancestor/Solution.java11-15 |
| C++ | if (!root || root == p || root == q) | TreeNode* left = lowestCommonAncestor(root->left, p, q); | lcci/04.08.First Common Ancestor/Solution.cpp12-16 |
| Go | if root == nil || root == p || root == q | left := lowestCommonAncestor(root.Left, p, q) | lcci/04.08.First Common Ancestor/Solution.go5-9 |
| JavaScript | if (!root || root === p || root === q) | const left = lowestCommonAncestor(root.left, p, q); | lcci/04.08.First Common Ancestor/Solution.js7-11 |
State management and backtracking:
For backtracking problems with explicit state restoration:
Sources: lcci/04.08.First Common Ancestor/Solution.py1-18 lcci/04.08.First Common Ancestor/Solution.java1-19 lcci/04.08.First Common Ancestor/Solution.cpp1-21 lcci/04.08.First Common Ancestor/Solution.go1-17 lcci/04.08.First Common Ancestor/Solution.js1-13
Time complexity patterns:
| Pattern | Complexity | Reasoning |
|---|---|---|
| Tree DFS | $O(n)$ | Visit each node once |
| N-Queens | $O(N!)$ | Factorial branching with pruning |
| Subsets | $O(2^n)$ | Binary choice for each element |
| Permutations | $O(n \cdot n!)$ | $n!$ permutations, $O(n)$ to copy each |
| Combination Sum | $O(n^{target/min})$ | Branching factor $n$, depth $target/min$ |
Space complexity:
Optimization techniques:
Sources: lcci/04.08.First Common Ancestor/README.md29-31 lcci/04.08.First Common Ancestor/README_EN.md77-79
| Pitfall | Problem | Solution |
|---|---|---|
| Forgetting to backtrack | State carries over between branches | Always restore state after recursion |
| Shallow copy in solutions | Modifications affect recorded solutions | Use path[:] or path.copy() in Python |
| Redundant constraint checks | Checking constraints after making choice | Check constraints before making choice |
| Inefficient pruning | Exploring invalid branches too deep | Add constraint checks at each level |
| Stack overflow | Recursion too deep | Consider iterative approach or increase stack limit |
Example of correct backtracking pattern:
Sources: General backtracking best practices
When to use DFS/Backtracking:
When to use alternatives:
Sources: Overall repository structure and pattern relationships from page TOC
Refresh this wiki
This wiki was recently refreshed. Please wait 6 days to refresh again.