全排列问题的回溯法实现与决策树剪枝
全排列问题要求生成数组所有可能的排列组合。当存在重复元素时,需要通过剪枝避免重复解。以下是基于回溯法的实现,包含决策树剪枝优化:
def permute(nums):
res = []
nums.sort() # 关键步骤:排序使相同元素相邻
used = [False] * len(nums) # 标记元素使用状态
def backtrack(path):
# 终止条件:路径长度等于数组长度
if len(path) == len(nums):
res.append(path[:]) # 深拷贝当前路径
return
for i in range(len(nums)):
# 剪枝条件1:跳过已使用的元素
if used[i]:
continue
# 剪枝条件2:跳过重复分支决策
if i > 0 and nums[i] == nums[i-1] and not used[i-1]:
continue
# 做选择
used[i] = True
path.append(nums[i])
# 递归进入下一层
backtrack(path)
# 撤销选择(回溯)
used[i] = False
path.pop()
backtrack([])
return res
剪枝策略详解
-
重复元素剪枝(核心优化)
if i > 0 and nums[i] == nums[i-1] and not used[i-1]: continuenums[i] == nums[i-1]:检测相邻重复元素not used[i-1]:确保前一个相同元素未被使用- 决策树效果:当遇到连续相同元素时,只允许从左向右顺序选择,避免产生对称重复分支
-
已选元素剪枝
if used[i]: continue直接跳过已使用的元素,避免无效搜索
时间复杂度分析
- 最坏情况(无重复元素):$O(n \times n!)$
- 最优情况(全相同元素):$O(n)$
- 剪枝使平均复杂度降低 $50%$ 以上
测试案例
print(permute([1,2,3])) # 6种排列
print(permute([1,1,2])) # 3种排列(剪枝后)
决策树剪枝可视化
以输入 [1,1,2] 为例:
原始决策树 剪枝后决策树
○ ○
/ | \ / \
1 1 2 1 2
/ \ / \ / \ / \ \
1 2 1 2 1 1 1 2 1
...(6个叶子) ...(3个叶子)
剪枝后跳过灰色分支,避免生成 [1,1,2] 的重复解
关键提示:排序操作是剪枝的前提条件,确保相同元素相邻,使
nums[i]==nums[i-1]能有效检测重复值。
269

被折叠的 条评论
为什么被折叠?



