Core Java

Backtracking algorithm

Greetings! This tutorial will focus on backtracking, a crucial technique for solving recursive problems. In programming, recursive functions are those that call themselves multiple times.

1. Introduction

Backtracking is an algorithmic technique that utilizes a brute-force approach to find the desired solution. Put simply, it exhaustively tries all possible solutions and selects the optimal one. The term “backtracking” refers to the process of retracing one’s steps and exploring other alternatives if the current solution is not viable. As a result, recursion is employed in this approach. Backtracking is particularly useful for solving problems with multiple solutions.

1.1 How does it work?

Backtracking is a systematic approach that involves attempting various sequences of solutions until a successful one is found. This technique uses the depth-first search method, in which a bounding function is applied during solution exploration to verify whether the current solution satisfies the given constraints. If it does, the algorithm continues searching. If not, the branch is pruned, and the algorithm backtracks to the previous level. In the backtracking algorithm, a tree represents all the possible states (solution or non-solution) of the problem from the initial state at the root to the terminal state at the leaf. The following algorithm outlines this process.

Backtracking algorithm

Backtrack(x)
    if x is not a solution
        return false
    if x is a new solution
        add to a list of solutions
    backtrack(expand x)

1.2 Applications of backtracking

  • N-queen problem
  • A sum of subset problem
  • Graph coloring
  • Hamilton cycle

1.3 Different between backtracking and recursion

Backtracking and recursion are related concepts in Java, but they serve distinct purposes.

  • Recursion refers to the technique of defining a function in terms of itself. In Java, a recursive function is a function that calls itself, typically with modified input parameters, until a specific termination condition is met. Recursion is useful for solving problems that have a recursive structure, such as computing the factorial of a number or traversing a tree data structure
  • On the other hand, backtracking is an algorithmic technique that utilizes recursion to explore all possible solutions to a problem. It works by incrementally building a solution and then backtracking to try alternative solutions if the current solution is found to be incorrect. Backtracking is often used to solve problems such as generating all possible permutations or solving constraint satisfaction problems like the N-Queens puzzle
  • In summary, recursion is a programming technique that involves defining a function in terms of itself, while backtracking is an algorithmic technique that uses recursion to solve problems by exploring all possible solutions

2. Determine if a problem can be solved using backtracking

Determining whether a problem can be solved using backtracking depends on a few factors. Here are some general guidelines to consider:

  • The problem should have multiple solutions or paths to a solution. Backtracking is typically used when a problem has a large solution space that can’t be explored exhaustively using other techniques
  • The problem should have a well-defined set of constraints that can be used to eliminate invalid solutions or paths early in the search process
  • The problem should have a recursive structure that can be exploited by the backtracking algorithm. This means that the problem can be decomposed into smaller subproblems that can be solved using a similar approach
  • The problem should have an efficient way to check if a partial solution is valid or not. In other words, there should be a way to determine whether a partial solution can be extended to a complete solution, or whether it should be abandoned and backtracked to an earlier state

If a problem satisfies these criteria, backtracking can likely be used to solve it. However, it’s important to note that backtracking is not always the best or most efficient approach, and other techniques such as dynamic programming or branch and bound may be more suitable for certain types of problems.

3. Backtracking algorithm example

Suppose we have a set of integers and we want to find all possible subsets that add up to a given target sum. To solve this problem using backtracking, we can follow these steps:

  • Start with an empty subset and a sum of 0
  • Consider adding the next integer to the subset
  • If adding the integer would not cause the sum to exceed the target sum, add it to the subset and recursively explore the remaining integers and target sum
  • If adding the integer would cause the sum to exceed the target sum, abandon the current path and backtrack to the previous state
  • When all possible subsets have been explored, return the list of subsets that add up to the target sum

Here’s a sample Java code that implements this backtracking algorithm.

Sample code

public List<List<Integer>> subsetsWithTargetSum(int[] nums, int targetSum) {
    List<List<Integer>> result = new ArrayList<>();
    backtrack(nums, targetSum, 0, new ArrayList<Integer>(), result);
    return result;
}

private void backtrack(int[] nums, int targetSum, int index, List<Integer> subset, List<List<Integer>> result) {
    if (targetSum == 0) {
        result.add(new ArrayList<>(subset));
        return;
    }

    for (int i = index; i < nums.length; i++) {
        int num = nums[i];
        if (targetSum - num >= 0) {
            subset.add(num);
            backtrack(nums, targetSum - num, i + 1, subset, result);
            subset.remove(subset.size() - 1);
        }
    }
}

This code uses a recursive backtracking function backtrack() to explore all possible subsets of nums that add up to targetSum. The function maintains a current subset of integers and a current sum, and it checks whether adding the next integer to the subset would cause the sum to exceed the target sum. If it wouldn’t, the function adds the integer to the subset and recursively explores the remaining integers. If it would, the function backtracks to the previous state and tries a different path. When a valid subset is found, it is added to the result list. This concludes our tutorial, and I trust that the article provided you with the information you sought. I wish you happy learning and encourage you to share your newfound knowledge with others!

4. Summary

This tutorial covered the practical application of backtracking in Java. You can download the source code from the Downloads section.

5. Download the Project

This tutorial aimed to provide an understanding of backtracking in Java and demonstrate how to implement it.

Download
You can download the full source code of this example here: Backtracking algorithm

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button