Day 20: Go Recursion and Backtracking

Venkat Annangi
Venkat Annangi
26/11/2024 15:36 4 min read 42 views
#golang series # go recursion # go backtracking #108 days of golang

Part of Series: 108 days of Golang

Article 22 of 22
Day 20: Go Recursion and Backtracking

Recursion and Backtracking in Go

Explore recursion and backtracking techniques to solve complex problems in Go.

Introduction to Recursion

Recursion is a fundamental concept in programming where a function calls itself repeatedly until it reaches a base case that stops the recursion. It is a powerful technique for solving problems that have a recursive structure.

package main

import "fmt"

func factorial(n int) int {
    if n == 0 {
        return 1
    }
    return n * factorial(n-1)
}

func main() {
    fmt.Println(factorial(5)) // Output: 120
}

In this example, the `factorial` function calls itself with decreasing values of `n` until it reaches the base case of `n == 0`, at which point it starts returning the results back up the call stack.

How Recursion Works

Recursion works by creating a new stack frame each time a function calls itself. This stack frame contains the local variables and parameters of the function. When the function returns, its stack frame is destroyed, and the previous stack frame is restored.

Here is a step-by-step breakdown of how recursion works:

  1. The function calls itself with a new set of parameters.
  2. A new stack frame is created, and the function's local variables and parameters are initialized.
  3. The function executes until it reaches a recursive call or a base case.
  4. If it reaches a recursive call, the function calls itself again with a new set of parameters.
  5. If it reaches a base case, the function returns a value to the previous stack frame.
  6. The previous stack frame is restored, and the function continues executing with the returned value.

Backtracking Example: Solving N-Queens

The N-Queens problem is a classic example of a problem that can be solved using backtracking. The problem is to place N queens on an NxN chessboard such that no two queens attack each other.

package main

import "fmt"

func solveNQueens(n int) [][]string {
    board := make([][]string, n)
    for i := range board {
        board[i] = make([]string, n)
        for j := range board[i] {
            board[i][j] = "."
        }
    }

    var result [][]string
    placeQueens(board, 0, &result)
    return result
}

func placeQueens(board [][]string, row int, result *[][]string) {
    if row == len(board) {
        temp := make([][]string, len(board))
        for i, r := range board {
            temp[i] = make([]string, len(r))
            copy(temp[i], r)
        }
        *result = append(*result, temp)
        return
    }

    for col := range board[row] {
        if isValid(board, row, col) {
            board[row][col] = "Q"
            placeQueens(board, row+1, result)
            board[row][col] = "."
        }
    }
}

func isValid(board [][]string, row, col int) bool {
    for i := 0; i < row; i++ {
        if board[i][col] == "Q" {
            return false
        }
    }

    for i, j := row-1, col-1; i >= 0 && j >= 0; i, j = i-1, j-1 {
        if board[i][j] == "Q" {
            return false
        }
    }

    for i, j := row-1, col+1; i >= 0 && j < len(board); i, j = i-1, j+1 {
        if board[i][j] == "Q" {
            return false
        }
    }

    return true
}

func main() {
    fmt.Println(solveNQueens(4))
}

In this example, the `solveNQueens` function uses backtracking to place queens on the board. It starts by initializing an empty board and then calls the `placeQueens` function to place queens on the board one row at a time.

Best Practices for Recursion and Backtracking

Here are some best practices to keep in mind when using recursion and backtracking:

  • Always define a clear base case that stops the recursion.
  • Use memoization or caching to avoid redundant calculations.
  • Use iterative solutions instead of recursive solutions when possible.
  • Avoid using recursion for large problems, as it can cause stack overflow errors.
  • Use backtracking to solve problems that have a recursive structure.

Common Pitfalls

Here are some common pitfalls to watch out for when using recursion and backtracking:

  • Infinite recursion: This occurs when a function calls itself without a base case, causing the function to call itself indefinitely.
  • Stack overflow: This occurs when a function calls itself too many times, causing the stack to overflow.
  • Redundant calculations: This occurs when a function calculates the same result multiple times, causing unnecessary computation.

Conclusion

Recursion and backtracking are powerful techniques for solving complex problems in Go. By understanding how recursion works and using best practices, you can write efficient and effective recursive solutions. Additionally, by using backtracking, you can solve problems that have a recursive structure.

Comments