权排列回溯算法
解决一个回溯问题,实际上就是一个决策树的遍历过程。
你只需要思考 3 个问题:
- 路径:也就是已经做出的选择。
- 选择列表:也就是你当前可以做的选择。
- 结束条件:也就是到达决策树底层,无法再做选择的条件。
代码方面,回溯算法的框架:
for 选择 in 选择列表: # 做选择 将该选择从选择列表移除 路径.add(选择) backtrack(路径, 选择列表) # 撤销选择 路径.remove(选择) 将该选择再加入选择列表
至此,我们就通过全排列问题详解了回溯算法的底层原理。当然,这个算法解决全排列不是很高效,应为对链表使用contains方法需要 O(N) 的时间复杂度。有更好的方法通过交换元素达到目的,但是难理解一些,这里就不写了,有兴趣可以自行搜索一下。
但是必须说明的是,不管怎么优化,都符合回溯框架,而且时间复杂度都不可能低于 O(N!),因为穷举整棵决策树是无法避免的。这也是回溯算法的一个特点,不像动态规划存在重叠子问题可以优化,回溯算法就是纯暴力穷举,复杂度一般都很高。
leetcode每日推荐
链接:[电话号码的字母组合]
class Solution: def letterCombinations(self, digits: str) -> List[str]: dic = { '2':['a','b','c'], '3':['d','e','f'], '4':['g','h','i'], '5':['j','k','l'], '6':['m','n','o'], '7':['p','q','r','s'], '8':['t','u','v'], '9':['w','x','y','z'] } result = [] # 结果 res = [] # 没此的便利 if digits == "": return [] def backtrack(i): if i == len(digits): # 满足条件 result.append("".join(res)) return else: for d in dic[digits[i]]: res.append(d) backtrack(i + 1) # 继续回溯 到满足条件 res.pop() # 吧满足条件的最后一位弹出 继续回溯 backtrack(0) return result