回溯算法
1.概念
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
2.基本思想
在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。(其实回溯法就是对隐式图的深度优先搜索算法)。
若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。
而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。
3.核心框架
for 选择 in 选择列表:
# 做选择
将该选择从选择列表移除
路径.add(选择)
backtrack(路径, 选择列表)
# 撤销选择
路径.remove(选择)
将该选择再加入选择列表
backtrack(路径, 选择列表)
是递归函数,在递归之前做出选择,在递归之后撤销刚才的选择,就能正确得到每个节点的选择列表和路径。
4.排列组合问题
实现代码
func permute(nums []int) [][]int {
// 记录全排列结果
res := make([][]int, 0)
track := make([]int, 0)
backtrack(nums, track, &res)
return res
}
// 判断切片nums中是否包含val
func contains(nums *[]int, val int) bool {
for i := 0; i < len(*nums); i++ {
if (*nums)[i] == val {
return true
}
}
return false
}
func backtrack(nums, track []int, ans *[][]int) {
// 触发结束条件
if len(track) == len(nums) {
(*ans) = append((*ans), track)
return
}
for i := 0; i < len(nums); i++ {
// 排除不合法的选择
if contains(&track, nums[i]) {
continue
}
// 做选择
track = append(track, nums[i])
// 进入下一层决策树
backtrack(nums, track, ans)
// 取消选择
track = track[:len(track)-1]
}
}
func main() {
s := []int{1, 2, 3}
ans := permute(s)
fmt.Println(ans)
}
5.电话号码的字母组合问题(LeetCode17)
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
实现代码
// 回溯算法
func letterCombinations2(digits string) []string {
table := []string{"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}
if len(digits) < 1 {
return []string{}
}
var track string
ans := make([]string, 0)
backtrack(0, &digits, &track, &table, &ans)
return ans
}
// 回溯算法中递归调用
func backtrack(pos int, digits, track *string, table, ans *[]string) {
// 退出条件
if len(*digits) == len(*track) {
*ans = append(*ans, *track)
return
}
currentDigit := (*digits)[pos] - '0' - 2
for i := 0; i < len((*table)[currentDigit]); i++ {
// 做选择
*track += string((*table)[currentDigit][i])
pos++
// 进入下一层决策树
backtrack(pos, digits, track, table, ans)
// 取消选择
*track = (*track)[:len(*track)-1]
pos--
}
}
func main() {
str := "236"
slice := letterCombinations2(str)
fmt.Println(slice)
}