排列 - DFS 和回溯 - 需要帮助理解展开和回溯

Posted

技术标签:

【中文标题】排列 - DFS 和回溯 - 需要帮助理解展开和回溯【英文标题】:Permutation- DFS and backtracking- need help in understanding unwinding and backtracking 【发布时间】:2019-02-19 19:14:02 【问题描述】:

以下代码用于实现 Ints 数组的排列。我无法理解这里的回溯是如何完成的——尤其是在我打印[1, 2, 3] 之后,我如何返回并打印[1, 3, 2]——path.removeLast() 究竟是如何工作的?

func permute(_ nums: [Int]) -> [[Int]] 
    var res = [[Int]]()
    var path = [Int]()
    var isVisited = [Bool](repeating: false, count: nums.count)
    var counter = 0
    dfs(&res, &path, &isVisited, nums)

    return res


private func dfs(_ res: inout [[Int]], _ path: inout [Int], _ isVisited: inout [Bool], _ nums: [Int]) 
    guard path.count != nums.count else 
        res.append(path)
        return
    

    for (i, num) in nums.enumerated() where !isVisited[i] 
        path.append(num)
        isVisited[i] = true
        dfs(&res, &path, &isVisited, nums)
        isVisited[i] = false
        path.removeLast()
    

【问题讨论】:

【参考方案1】:

有时通过一个例子来理解回溯是最容易的。取数组 [1,2,3],然后使用您的方法执行以下操作:

Before removing: 1
Before removing: 1 2
Before removing: 1 2 3
After removing: 1 2
After removing: 1
Before removing: 1 3
Before removing: 1 3 2
After removing: 1 3
After removing: 1
After removing:
Before removing: 2
Before removing: 2 1
Before removing: 2 1 3
After removing: 2 1
After removing: 2
Before removing: 2 3
Before removing: 2 3 1
After removing: 2 3
After removing: 2
After removing:
Before removing: 3
Before removing: 3 1
Before removing: 3 1 2
After removing: 3 1
After removing: 3
Before removing: 3 2
Before removing: 3 2 1
After removing: 3 2
After removing: 3
After removing:

实际上,您所做的是为每个排列生成所有可能的子序列,然后删除它们(因此删除最后一个),直到您返回一个空列表。如果您为您提供的代码绘制递归树,您将有 31 个节点(上面的每一行一个节点)。我们能做得更好吗?是的。对于同一示例,请考虑以下树:

(此处使用字符而不是整数的类似树的更漂亮版本:Permutation of string using backtracking algorithm)

一个很大的改进。树中只有 10 个节点,树中的最后一层具有所有排列。这可以使用回溯来完成,并且是一个更容易理解的示例。您所做的只是交换节点,而不是为每个排列生成所有可能的子序列。可以在此处找到第二种更好方法的 Swift 实现:https://leetcode.com/problems/permutations/discuss/229627/Swift

【讨论】:

以上是关于排列 - DFS 和回溯 - 需要帮助理解展开和回溯的主要内容,如果未能解决你的问题,请参考以下文章

leetcode算法题基础(四十三) 回溯算法总结

LeetCode 526 优美的排列[dfs 回溯] HERODING的LeetCode之路

dfs——n的全排列(回溯)

递归实现DFS全排列

剑指 Offer 38. 字符串的排列(dfs回溯实现全排列,Java)

回溯剪枝,dfs,bfs