返回
LeetCode 全排列 II 算法详解
前端
2023-11-13 13:36:51
LeetCode算法学习之旅--Recursion--47. 全排列 II
在本文中,我们将踏上一个激动人心的LeetCode之旅,深入探索第47题:全排列II。这是一个中等难度的问题,非常适合初级算法学习者巩固他们的基本功。
问题
给你一个包含数字 1 - n 的数组 nums,其中某些数字是重复的。请输出所有可能的全排列。
分析
这道题考查的是排列组合的思想,我们需要找到所有可能的排列组合,并剔除其中重复的元素。我们可以使用递归的方法来解决这个问题。
解法一:递归
递归的核心思想是将一个复杂的问题分解成一系列较小的子问题,然后对这些子问题递归求解,最终组合出问题的解。
- 基本情况: 当数组nums为空时,返回一个空列表[]。
- 递归步骤: 对于数组nums中的每个元素num,将num插入到当前排列的所有可能位置中,然后递归求解剩余元素的全排列。
- 剪枝优化: 由于数组中存在重复元素,我们需要剪枝重复的排列。当我们插入一个元素num时,如果该元素与前一个元素相等,则跳过当前插入位置。
代码示例:
def permuteUnique(nums):
result = []
nums.sort() # 对数组排序,以便剪枝重复排列
backtrack(nums, [], result)
return result
def backtrack(nums, permutation, result):
if not nums:
result.append(permutation.copy())
return
for i in range(len(nums)):
# 剪枝重复排列
if i > 0 and nums[i] == nums[i - 1]:
continue
permutation.append(nums[i])
backtrack(nums[:i] + nums[i + 1:], permutation, result)
permutation.pop()
复杂度分析
- 时间复杂度:O(n * n!)。对于每个元素,我们有n个插入位置,并且有n!个排列。
- 空间复杂度:O(n),用于递归调用栈。
解法二:暴力
暴力解法比较直观,我们可以枚举所有可能的排列组合,然后检查每个排列是否包含重复元素。如果包含重复元素,则将其丢弃。
代码示例:
def permuteUnique(nums):
result = []
visited = set()
permutation = []
backtrack(nums, visited, permutation, result)
return result
def backtrack(nums, visited, permutation, result):
if len(permutation) == len(nums):
result.append(permutation.copy())
return
for i in range(len(nums)):
if i in visited:
continue
visited.add(i)
permutation.append(nums[i])
backtrack(nums, visited, permutation, result)
visited.remove(i)
permutation.pop()
复杂度分析
- 时间复杂度:O(n^n)。我们有n个元素,每个元素可以放置在n个位置,因此共有n^n个排列。
- 空间复杂度:O(n),用于递归调用栈。
总结
本题考察了递归和剪枝的思想,以及暴力解法的实现。在实际应用中,我们通常使用递归解法,因为它可以避免重复计算,提高效率。