LeetCode46 全排列(详解回溯法)

68 阅读2分钟

leetcode.cn/problems/pe…

image.png

解法一:回溯法

有关递归的算法,都离不开“树”的遍历这一抽象模型,只不过对于不同的算法,在前(中)后序遍历的时候,所做的事不同而已。

实际上回溯算法就是一个 N 叉树的前序遍历加上后序遍历而已。这个 N 等于当前可做的选择(choices)的总数,同时,在前序遍历的位置作出当前选择(choose 过程),然后开始递归,最后在后序遍历的位置撤销当前选择(unchoose 过程)。

// 二叉树遍历框架  
def traverse(root):  
    if root is Nonereturn  
    # 前序遍历代码写在这  
    traverse(root.left)  
    # 中序遍历代码写在这   
    traverse(root.right)  
    # 后序遍历代码写在这  
  
// N 叉树遍历框架  
def traverse(root):  
    if root is Nonereturn  
    for child in root.children:  # 多个孩子,遍历递归
        # 前序遍历代码写在这  
        traverse(child)  
        # 后序遍历代码写在这
        
/*
回溯伪代码
choiceList:当前可以进行的选择列表
track:可以理解为决策路径,即已经做出一系列选择
res:用来储存答案,即符合条件的决策路径
*/
def backtrack(choiceList, track, res):  
    if track is OK:
        res = append(res, track)  
    else:  
        for choice in choiceList:  
            # choose:选择一个 choice 加入 track  
            backtrack(choices, track, answer)  
            # unchoose:从 track 中撤销上面的选择,该选择又重新加回选择列表

每向下走一层,就是在「选择列表」中挑一个「选择」加入「决策路径」,然后把这个选择从「选择列表」中删除(以免之后重复选择);当一个决策分支探索完成后,我们就要向上回溯,要把该分支的「选择」从「决策列表」中取出,然后把这个「选择」重新加入「选择列表」(供其他的决策分支使用)。以上,就是模板中 choose 和 unchoose 的过程,choose 过程是向下探索,进行选择;unchoose 过程是向上回溯,撤销刚才的选择

可以理解为,回溯算法相当于一个决策过程,递归地遍历一棵决策树,穷举所有的决策,同时把符合条件的决策挑出来。

写 backtrack 函数时,需要维护走过的「路径」和当前可以做的「选择列表」,当触发「结束条件」时,将「路径」记入结果集。

回到本题,那么这棵决策树如下:

image.png

image.png 对照着写成代码

func permute(nums []int) [][]int {
    var res [][]int
    if len(nums) == 0{
        return res
    }
    used := make([]bool, len(nums))
    path := make([]int, 0, len(nums))
    backtrack(nums, used, path, &res)
    return res
}

// 由于切片不方便随机删除和恢复某个元素,这里借助一个标记数组
// 可选择列表choiceList即为输入nums数组中未被标记为used的元素
func backtrack(nums []int, used []bool, path []int, res *[][]int) {
    if len(path) == len(nums){
        tmp := make([]int, len(path))
        copy(tmp, path)
        *res = append(*res, tmp)
        return
    }
    for idx, num := range nums{
        if used[idx]{
            continue
        }
        path = append(path, num)
        used[idx] = true
        backtrack(nums, used, path, res)
        path = path[:len(path)-1]
        used[idx] = false
    }
}

参考

mp.weixin.qq.com/s/trILKSiN9…

labuladong.online/algo/essent…