是否有人理解以下迭代算法来生成数字列表的所有排列?
我不理解while len(stack)
循环中的逻辑。有人可以解释它是如何工作的吗?
# Non-Recursion
@param nums: A list of Integers.
@return: A list of permutations.
def permute(self, nums):
if nums is None:
return []
nums = sorted(nums)
permutation = []
stack = [-1]
permutations = []
while len(stack):
index = stack.pop()
index += 1
while index < len(nums):
if nums[index] not in permutation:
break
index += 1
else:
if len(permutation):
permutation.pop()
continue
stack.append(index)
stack.append(-1)
permutation.append(nums[index])
if len(permutation) == len(nums):
permutations.append(list(permutation))
return permutations
我只是想了解上面的代码。
答案 0 :(得分:2)
正如您的问题的评论部分所述,调试可能提供了一种有用的方法来理解代码的作用。但是,让我提供一个关于代码功能的高级视角。
首先,尽管没有对函数置换的递归调用,但是您提供的代码实际上是递归的,因为它只是保留自己的堆栈,而不是使用操作系统内存管理器提供的代码。具体来说,变量堆栈保留了“递归数据”,可以说是从一个递归调用传递到另一个递归调用。你可以,也许应该考虑 permute 函数中外部while循环的每次迭代作为递归调用。如果你这样做,你会看到外部while循环有助于“递归地”以深度优先的方式遍历 nums 的每个排列。
注意到这一点,很容易弄清楚每个“递归调用”的作用。基本上,变量置换保持 nums 的当前排列,这是在循环进行时形成的。变量排列存储找到的 nums 的所有排列。正如您所看到的,只有当 len(置换)等于 len(nums)时才会更新置换,这可以被视为基本情况使用自定义堆栈实现的重复关系。最后,内部while循环基本上选择 nums 中的哪个元素添加到正在形成的当前排列(即存储在变量置换中)。
这就是它,真的。根据建议,您可以使用调试器找出与维护堆栈相关的线路上正在完成的操作。最后,请允许我重复一遍,我个人不认为这种实现是非递归的。事实恰恰相反,这种递归解决方案不是使用操作系统提供的抽象,而是保留自己的堆栈。为了更好地理解正确的非递归解决方案,您可以观察到下面提供的找到n th Fibonacci数的问题的递归和迭代解决方案的差异。正如您所看到的,非递归解决方案不会保留堆栈,而是将问题划分为较小的实例(递归),而是从较小的解决方案构建解决方案。 (动态编程)
def recursive_fib(n):
if n == 0:
return 0
return recursive_fib(n-1) - recursive_fib(n-2)
def iterative_fib(n):
f_0 = 0
f_1 = 1
for i in range(3, n):
f_2 = f_1 + f_0
f_0 = f_1
f_1 = f_2
return f_1
答案 1 :(得分:0)
@ilim的答案是正确的,应该是接受的答案,但我只是想添加另一个不适合作为评论的观点。虽然我想你正在研究这个算法作为练习,但应该指出,根据列表的大小,更好的方法可以是用户itertools
的{{1}}函数:< / p>
permutations()
在我的机器上使用11项(39m排列)的列表进行测试,使用print [x for x in itertools.permutations([1, 2, 3])]
需要1.7秒,但使用上面的自定义解决方案需要76秒。但请注意,如果有12个项目(479m排列),itertools.permutations(x)
解决方案会因内存错误而崩溃。如果您需要有效地生成这种大小的排列,您可能最好放弃本机代码。