WikiGalaxy

Personalize

Backtracking vs Recursion

Understanding Recursion:

Recursion is a technique in programming where a function calls itself to solve a smaller instance of the same problem. It's a powerful concept used to break down complex problems into simpler ones.

Understanding Backtracking:

Backtracking is a refinement of recursion where you try to build a solution incrementally and abandon a path as soon as you determine it cannot lead to a valid solution. It's often used for solving constraint satisfaction problems like puzzles, mazes, and the n-queens problem.

    Step 1: Diagram of Recursive Call Stack
    Step 2: Diagram of Backtracking Decision Tree
    Step 3: Diagram showing Pruning in Backtracking
    Step 4: Diagram showing Recursive Base Case
  

      // Example of Recursion: Factorial Calculation
      int factorial(int n) {
        if (n == 0) return 1;
        return n * factorial(n - 1);
      }

      // Example of Backtracking: N-Queens Problem
      boolean solveNQueens(int board[][], int col) {
        if (col >= N) return true;
        for (int i = 0; i < N; i++) {
          if (isSafe(board, i, col)) {
            board[i][col] = 1;
            if (solveNQueens(board, col + 1)) return true;
            board[i][col] = 0; // BACKTRACK
          }
        }
        return false;
      }
    

Recursive Approach:

Recursion simplifies the code for problems that can be broken down into similar sub-problems. It is elegant but can lead to performance issues due to repeated calculations and stack overflow errors.

Backtracking Approach:

Backtracking is efficient for problems with constraints, as it prunes paths that are not viable early on. It is often more complex to implement than simple recursion but provides better performance for certain types of problems.

Console Output:

Factorial of 5: 120

N-Queens Solution Exists: true

Example 1: Fibonacci Sequence Using Recursion

Concept:

The Fibonacci sequence is a series of numbers where each number is the sum of the two preceding ones. It's a classic example of a problem that can be solved using recursion.

    Step 1: Diagram of Recursive Calls for Fibonacci
    Step 2: Diagram showing Base Cases (Fibonacci(0) and Fibonacci(1))
    Step 3: Diagram showing Recursive Case (Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2))
  

      // Recursive Fibonacci Function
      int fibonacci(int n) {
        if (n <= 1) return n;
        return fibonacci(n - 1) + fibonacci(n - 2);
      }
    

Explanation:

The recursive function calls itself to calculate the Fibonacci number for n-1 and n-2 until it reaches the base cases. This approach is simple but inefficient for large n due to repeated calculations.

Console Output:

Fibonacci of 5: 5

Example 2: Subset Sum Problem Using Backtracking

Concept:

The subset sum problem involves determining if there is a subset of a given set with a sum equal to a given number. Backtracking is used to explore all possible subsets.

    Step 1: Diagram of Subset Construction
    Step 2: Diagram showing Decision to Include/Exclude Element
    Step 3: Diagram showing Pruning when Sum Exceeds Target
  

      // Backtracking Subset Sum Function
      boolean isSubsetSum(int set[], int n, int sum) {
        if (sum == 0) return true;
        if (n == 0) return false;
        if (set[n - 1] > sum) return isSubsetSum(set, n - 1, sum);
        return isSubsetSum(set, n - 1, sum) || isSubsetSum(set, n - 1, sum - set[n - 1]);
      }
    

Explanation:

The function explores all subsets by including or excluding each element and backtracks when the current subset exceeds the target sum. This approach efficiently finds the solution by pruning non-promising paths.

Console Output:

Subset with Sum Exists: true

Example 3: Permutations Using Backtracking

Concept:

Generating all permutations of a set involves rearranging its elements in every possible way. Backtracking is used to systematically explore each permutation.

    Step 1: Diagram of Permutation Tree
    Step 2: Diagram showing Swap Operation
    Step 3: Diagram showing Backtracking after Swap
  

      // Backtracking Permutation Function
      void permute(int arr[], int l, int r) {
        if (l == r) System.out.println(Arrays.toString(arr));
        else {
          for (int i = l; i <= r; i++) {
            swap(arr, l, i);
            permute(arr, l + 1, r);
            swap(arr, l, i); // BACKTRACK
          }
        }
      }
    

Explanation:

The function generates permutations by swapping elements and recursively calling itself to fix the next position. After exploring a permutation, it backtracks by swapping back to restore the original order.

Console Output:

[1, 2, 3]

[1, 3, 2]

[2, 1, 3]

[2, 3, 1]

[3, 1, 2]

[3, 2, 1]

Example 4: Solving Sudoku Using Backtracking

Concept:

Sudoku is a logic-based puzzle where the goal is to fill a 9x9 grid with digits so that each column, row, and 3x3 section contain all the numbers between 1 and 9. Backtracking is used to attempt different numbers and backtrack when a conflict is found.

    Step 1: Diagram of Sudoku Grid
    Step 2: Diagram showing Attempt to Place Number
    Step 3: Diagram showing Backtracking on Conflict
  

      // Backtracking Sudoku Solver
      boolean solveSudoku(int grid[][], int row, int col) {
        if (row == N - 1 && col == N) return true;
        if (col == N) {
          row++;
          col = 0;
        }
        if (grid[row][col] != 0) return solveSudoku(grid, row, col + 1);
        for (int num = 1; num <= N; num++) {
          if (isSafe(grid, row, col, num)) {
            grid[row][col] = num;
            if (solveSudoku(grid, row, col + 1)) return true;
            grid[row][col] = 0; // BACKTRACK
          }
        }
        return false;
      }
    

Explanation:

The solver attempts to place each number in a cell and recursively tries to solve the rest of the grid. If a conflict arises, it backtracks by resetting the cell and trying a different number.

Console Output:

Sudoku Solved Successfully

Example 5: Maze Solver Using Backtracking

Concept:

A maze solver finds a path from the start to the end of a maze. Backtracking is used to try different paths and backtrack when a dead end is reached.

    Step 1: Diagram of Maze Grid
    Step 2: Diagram showing Path Exploration
    Step 3: Diagram showing Backtracking on Dead End
  

      // Backtracking Maze Solver
      boolean solveMaze(int maze[][], int x, int y) {
        if (x == N - 1 && y == N - 1) return true;
        if (isSafe(maze, x, y)) {
          maze[x][y] = 1;
          if (solveMaze(maze, x + 1, y)) return true;
          if (solveMaze(maze, x, y + 1)) return true;
          maze[x][y] = 0; // BACKTRACK
          return false;
        }
        return false;
      }
    

Explanation:

The solver recursively explores paths by moving right or down. If it reaches a dead end, it backtracks by marking the cell as unvisited and tries a different direction.

Console Output:

Path Found: true

Example 6: Palindrome Partitioning Using Backtracking

Concept:

Palindrome partitioning involves dividing a string into substrings such that each substring is a palindrome. Backtracking is used to explore different partitioning schemes.

    Step 1: Diagram of String Partitioning
    Step 2: Diagram showing Palindrome Check
    Step 3: Diagram showing Backtracking on Non-Palindrome
  

      // Backtracking Palindrome Partitioning
      void partition(String s, int start, List currentList, List> result) {
        if (start >= s.length()) result.add(new ArrayList<>(currentList));
        for (int end = start; end < s.length(); end++) {
          if (isPalindrome(s, start, end)) {
            currentList.add(s.substring(start, end + 1));
            partition(s, end + 1, currentList, result);
            currentList.remove(currentList.size() - 1); // BACKTRACK
          }
        }
      }
    

Explanation:

The function explores different partitions by checking for palindromes and recursively partitioning the remaining string. It backtracks by removing the last added substring when a partitioning doesn't lead to a solution.

Console Output:

Palindrome Partitions: [["a","a","b"],["aa","b"]]

Example 7: Word Search on Grid Using Backtracking

Concept:

The word search problem involves finding a given word in a grid of letters. Backtracking is used to explore potential paths through the grid.

    Step 1: Diagram of Grid with Starting Point
    Step 2: Diagram showing Path Exploration for Word
    Step 3: Diagram showing Backtracking on Incorrect Path
  

      // Backtracking Word Search
      boolean exist(char[][] board, String word) {
        for (int i = 0; i < board.length; i++) {
          for (int j = 0; j < board[0].length; j++) {
            if (dfs(board, word, i, j, 0)) return true;
          }
        }
        return false;
      }

      boolean dfs(char[][] board, String word, int i, int j, int index) {
        if (index == word.length()) return true;
        if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(index)) return false;
        char temp = board[i][j];
        board[i][j] = ' ';
        boolean found = dfs(board, word, i + 1, j, index + 1)
                      || dfs(board, word, i - 1, j, index + 1)
                      || dfs(board, word, i, j + 1, index + 1)
                      || dfs(board, word, i, j - 1, index + 1);
        board[i][j] = temp; // BACKTRACK
        return found;
      }
    

Explanation:

The function explores all possible paths starting from each cell, recursively checking adjacent cells for the next letter in the word. It backtracks by restoring the cell's original value after exploring each path.

Console Output:

Word Found: true

Example 8: Combination Sum Using Backtracking

Concept:

The combination sum problem involves finding all unique combinations of numbers that sum up to a target. Backtracking is used to explore different combinations.

    Step 1: Diagram of Combination Construction
    Step 2: Diagram showing Inclusion of Element
    Step 3: Diagram showing Backtracking on Exceeding Target
  

      // Backtracking Combination Sum
      void findCombinations(int[] candidates, int target, List current, int start, List> result) {
        if (target == 0) result.add(new ArrayList<>(current));
        for (int i = start; i < candidates.length; i++) {
          if (candidates[i] <= target) {
            current.add(candidates[i]);
            findCombinations(candidates, target - candidates[i], current, i, result);
            current.remove(current.size() - 1); // BACKTRACK
          }
        }
      }
    

Explanation:

The function explores different combinations by adding each candidate to the current combination and recursively exploring further. It backtracks by removing the last added element when the target is exceeded or achieved.

Console Output:

Combinations Found: [[2, 2, 3], [7]]

logo of wikigalaxy

Newsletter

Subscribe to our newsletter for weekly updates and promotions.

Privacy Policy

 • 

Terms of Service

Copyright © WikiGalaxy 2025