1. Introduction

In this tutorial, we’ll explore the difference between backtracking and depth-first search. We’ll also look at an example algorithm using the backtracking technique.

Depth-first search (DFS) is the algorithm used to traverse a graph. It starts on the root node and travels as deep as possible along each branch.

The tree below presents the order in which nodes will be visited when applying depth-first search:

DFS

We can use the depth-first search to solve puzzles with only one solution or to find connected components. It’s also a default algorithm for finding cycles in a graph. Moreover, it’s a more useful general graph search than the breadth-first search because it’s more space-efficient.

3. Backtracking

Backtracking is an algorithm used to iterate over all possible solutions in the search space. Therefore, it’s commonly used to solve problems with constraints. In this sense, backtracking limits the search space, thus saving compute time.

The process of constructing partial solutions from the base case corresponds to performing a depth search traversal of the tree.

3.1. An Example

Let’s write a straightforward algorithm to generate all subsets from elements in the given array. For each input element, we will add it to the candidate list and recursively invoke the Subsets() function. After this step, we backtrack by removing this element from our candidate list. Finally, we call Subsets() function again.

The ultimate result is a sum of both recursive calls.

algorithm findAllSubsets(Q, k, candidateSubset):
    // INPUT
    //    Q = An array of n unique elements
    //    k = Current index in the array Q
    //    candidateSubset = Current subset being constructed
    // OUTPUT
    //    A list of all possible subsets of the array Q without duplicates
    
    if k = length(Q):
        result <- create an empty list
        add candidateSubset to result
        return result
    else:
        include the current element in candidateSubset
        subresultWithElement <- findAllSubsets(Q, k + 1, candidateSubset)
        
        exclude the current element from candidateSubset
        subresultWithoutElement <- findAllSubsets(Q, k + 1, candidateSubset)
        
        combine both subresults
        result <- subresultWithElement + subresultWithoutElement
        return result

The recursive execution tree of the above algorithm will look as follows:

DFS Page 2

We can find the final solution at the bottom of the execution tree. It’s a set of eight elements:

    \[((123), (12), (13), (1), (23), (2), (3), ())\]

The subsets order shows that to construct them, we used depth-first search following the left tree branch.

3.2. Applications of Backtracking

Backtracking is widely used to solve crosswords, Sudoku, chess, tic-tac-toe, and other puzzles. It’s also useful when generating all the combinations of elements from a given set. Additionally, it’s the basis of programming languages such as Icon, Planner, and Prolog.

4. Comparison

Interestingly, we can see that the solution space over which the backtracking algorithm iterates usually has a form of a graph. As such, the implementation of both algorithms is very similar:

Rendered by QuickLaTeX.com

5. Conclusion

In this article, we discussed the similarities and differences between backtracking and DFS. We also wrote a simple algorithm using backtracking.

Finally, we learned that because backtracking uses DFS to traverse the solution space, they can both be represented as recursive algorithms.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.