数据结构与算法之 leetcode 回溯算法全排列去重 排序 + 剪枝

数据结构与算法阿木 发布于 2025-07-12 10 次阅读


回溯算法全排列去重(排序 + 剪枝)在LeetCode中的应用

回溯算法是一种在解决问题时,通过递归尝试所有可能的路径,直到找到一条满足条件的路径或确定所有路径都不满足条件为止的算法。在LeetCode等编程竞赛平台中,全排列去重问题是一个常见的题型,它要求我们找出所有不重复的排列组合。本文将围绕这一主题,探讨如何使用回溯算法结合排序和剪枝技术来解决全排列去重问题。

全排列去重问题分析

全排列去重问题可以描述为:给定一个不包含重复数字的数组,返回其所有不重复的全排列。例如,对于输入数组 `[1, 2, 3]`,输出应为 `[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]`。

为了解决这个问题,我们可以采用以下步骤:

1. 对输入数组进行排序,确保重复的元素相邻,便于后续的剪枝操作。

2. 使用回溯算法生成所有可能的排列。

3. 在回溯过程中,通过剪枝避免重复的排列生成。

代码实现

以下是一个使用Python编写的全排列去重问题的解决方案:

python

def permuteUnique(nums):


def backtrack(start, end):


if start == end:


result.append(nums[:])


return


seen = set()


for i in range(start, end):


if nums[i] in seen:


continue


seen.add(nums[i])


nums[start], nums[i] = nums[i], nums[start]


backtrack(start + 1, end)


nums[start], nums[i] = nums[i], nums[start]

nums.sort()


result = []


backtrack(0, len(nums))


return result

测试代码


nums = [1, 2, 3]


print(permuteUnique(nums))


代码解析

1. 排序:首先对输入数组 `nums` 进行排序,确保重复的元素相邻。

2. 回溯函数 `backtrack`:

- `start` 和 `end` 分别表示当前递归的起始和结束索引。

- 如果 `start` 等于 `end`,说明当前排列已经完成,将其添加到结果列表 `result` 中。

- 使用一个集合 `seen` 来记录已经使用过的元素,避免重复的排列生成。

- 遍历从 `start` 到 `end` 的所有元素,如果当前元素已经在 `seen` 中,则跳过;否则,将其与 `start` 索引的元素交换,然后递归调用 `backtrack` 函数。

- 递归完成后,将交换后的元素再交换回来,以便后续的排列生成。

3. 调用 `backtrack` 函数:初始化 `result` 列表,并调用 `backtrack` 函数从索引 0 开始生成排列。

剪枝技术

在上述代码中,我们使用了剪枝技术来避免重复的排列生成。具体来说,通过以下步骤实现:

1. 排序:确保重复的元素相邻。

2. 使用集合 `seen`:在遍历过程中,如果当前元素已经在 `seen` 中,则跳过,避免重复的排列生成。

总结

本文介绍了如何使用回溯算法结合排序和剪枝技术来解决全排列去重问题。通过排序和剪枝,我们可以有效地减少不必要的排列生成,提高算法的效率。在实际应用中,我们可以根据具体问题调整排序和剪枝策略,以达到最佳的性能表现。

扩展

1. 处理包含重复数字的数组:如果输入数组包含重复数字,我们需要在排序后进行额外的处理,例如使用一个计数数组来记录每个数字的出现次数,并在回溯过程中避免重复的排列生成。

2. 优化空间复杂度:在回溯算法中,我们可以使用原地交换的方式生成排列,从而减少空间复杂度。

3. 应用场景:全排列去重问题在密码学、组合数学等领域有着广泛的应用,例如生成所有可能的密码组合、求解组合数学问题等。