数据结构与算法之 leetcode 回溯算法全排列 II 含重复元素排列

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


回溯算法全排列 II(含重复元素排列)在 LeetCode 中的实现

在算法和数据结构的学习过程中,回溯算法是一个非常重要的概念。它是一种通过递归尝试所有可能的路径来解决问题的方法。在 LeetCode 中,全排列问题是一个经典的回溯算法应用场景,特别是当存在重复元素时,问题变得更加复杂。本文将围绕全排列 II(含重复元素排列)这一主题,详细介绍在 LeetCode 中的实现方法。

全排列 II 问题分析

全排列 II 问题要求给出所有不同的排列组合,其中可能包含重复的元素。例如,对于输入的数组 `[1, 1, 2]`,我们需要返回所有不重复的排列,如 `[1, 1, 2]`、`[1, 2, 1]` 和 `[2, 1, 1]`。

解决思路

为了解决全排列 II 问题,我们可以采用以下步骤:

1. 剪枝:由于存在重复元素,我们需要在递归过程中剪枝,避免重复的排列出现。

2. 排序:对输入数组进行排序,这样相同的元素会相邻,便于我们在递归过程中进行剪枝。

3. 递归:使用回溯算法递归地构建排列。

代码实现

下面是使用 Python 实现的全排列 II 的代码:

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


代码解析

1. 排序:首先对输入数组 `nums` 进行排序,这样相同的元素会相邻。

2. 递归函数 `backtrack`:

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

- 如果 `start` 等于 `end`,说明找到了一个有效的排列,将其添加到结果列表 `result` 中。

- `seen` 是一个集合,用于存储当前递归过程中已经使用过的元素,避免重复。

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

- 递归完成后,将 `start` 和 `i` 索引的元素交换回原来的位置,以便进行下一次交换。

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

测试与优化

为了验证代码的正确性,我们可以使用一些测试用例进行测试:

python

print(permuteUnique([1, 1, 2])) [[1, 1, 2], [1, 2, 1], [2, 1, 1]]


print(permuteUnique([1, 2, 3])) [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]


在测试过程中,我们可以发现代码能够正确地处理包含重复元素的全排列问题。

总结

本文介绍了全排列 II(含重复元素排列)在 LeetCode 中的实现方法。通过排序和剪枝,我们可以有效地解决这一问题。在实际应用中,回溯算法是一种非常强大的工具,可以帮助我们解决许多复杂的问题。希望本文能够帮助读者更好地理解回溯算法及其在解决全排列问题中的应用。