算法篇:一个模板秒杀回溯

148 阅读3分钟

回溯算法介绍

回溯算法通常用于解决组合、切割、子集、排列等问题。本文将为你提供一个回溯算法的基本框架,它可以被用来解决这些类型的问题。该框架遵循三个主要步骤:选择、探索和撤销选择。

模板如下:

package main

import "fmt"

// 回溯函数的基本框架
func backtrack(path []int, selections []int, results *[][]int) {
    // 如果满足结束条件,将结果保存起来
    if isSolution(path) {
        solution := make([]int, len(path))
        copy(solution, path)
        *results = append(*results, solution)
        return
    }

    // 遍历所有选择
    for _, selection := range selections {
        // 做出选择
        path = append(path, selection)

        // 排除不合法的选择
        if !isValid(path) {
            path = path[:len(path)-1] // 撤销选择
            continue
        }

        // 进入下一层决策树
        backtrack(path, selections, results)

        // 撤销选择
        path = path[:len(path)-1]
    }
}

// isSolution 判断当前路径是否为解决方案
func isSolution(path []int) bool {
    // 根据具体问题实现结束条件
    // 例如:达到路径的特定长度,或满足特定的条件
    return false // 示例中默认返回false
}

// isValid 判断当前路径是否为有效选择
func isValid(path []int) bool {
    // 根据具体问题实现有效性检查
    // 例如:检查是否有重复元素,或者路径是否满足特定约束
    return true // 示例中默认返回true
}

func main() {
    // 初始化路径和结果容器
    var path []int
    var results [][]int

    // 初始化选择列表
    selections := []int{1, 2, 3} // 示例中的选择列表

    // 执行回溯
    backtrack(path, selections, &results)

    // 打印结果
    fmt.Println("所有可能的解决方案:", results)
}

本文中将带大家在不同场景下运用这个模板去解出不同的leetcode题目

leetcode 46 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入: nums = [1,2,3]
输出: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

输入: nums = [0,1]
输出: [[0,1],[1,0]]

示例 3:

输入: nums = [1]
输出: [[1]]
func permute(nums []int) [][]int {
   results:= make([][]int,0)
   backtrack([]int{},nums,len(nums),&results)
   return results
}

func backtrack(path []int,selections []int,maxLen int,results*[][]int) {
   if len(path) == maxLen {
      res := make([]int,len(path))
      copy(res,path)
      *results = append(*results,res)
      return
   }
   for idx,selection := range selections {
      path = append(path,selection)
      newSelections := make([]int,len(selections))
      copy(newSelections,selections)
      newSelections= Remove(newSelections,idx)
      backtrack(path,newSelections,maxLen,results)
      path = path[:len(path)-1]
   }
}
func Remove(nums []int,idx int) []int {
   nums = append(nums[0:idx],nums[idx+1:]...)
   return nums
}

leetcode 22 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入: n = 3
输出: ["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入: n = 1
输出: ["()"]

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入: n = 3
输出: ["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入: n = 1
输出: ["()"]
func generateParenthesis(n int) []string {
   if n == 0 {
      return nil
   }
   res := make([]string,0)
   backtrack("",n,n,&res)
   return res
}

func backtrack(str string,left,right int,res*[]string) {
   if left == 0 && right == 0 {
      *res = append(*res,str)
      return
   }
   if  left>0  {
      backtrack(str+"(",left-1,right,res)
   }
   if right>0 && right>left{
      backtrack(str+")",left,right-1,res)
   }

}

leetcode 77 组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

示例 2:

输入: n = 1, k = 1
输出: [[1]]
func combine(n int, k int) [][]int {
   if n < k {
      return nil
   }
   results := make([][]int,0)
   backtrack([]int{},n,k,1,&results)
   return results
}

func backtrack(combination []int,n,k,idx int,results*[][]int) {
   if len(combination) == k {
      res:= make([]int,k)
      copy(res,combination)
      *results = append(*results,res)
   }
   for i := idx;i <= n;i++ {
      combination = append(combination,i)
      backtrack(combination,n,k,i+1,results)
      combination = combination[:len(combination)-1]
   }
}