回溯算法

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)
}


posted @ 2020-08-19 09:31  白小白2020  阅读(91)  评论(0编辑  收藏  举报