【发布时间】:2018-08-23 12:23:26
【问题描述】:
我有两个函数,这两个函数都可以展平 Python 中任意嵌套的列表列表。
我正在尝试计算两者的时间复杂度,看看哪个更有效,但到目前为止我还没有找到任何关于 SO 的确定性。有很多关于列表列表的问题,但不是第 n 级嵌套。
函数 1(迭代)
def flattenIterative(arr):
i = 0
while i < len(arr):
while isinstance(arr[i], list):
if not arr[i]:
arr.pop(i)
i -= 1
break
else:
arr[i: i + 1] = arr[i]
i += 1
return arr
函数 2(递归)
def flattenRecursive(arr):
if not arr:
return arr
if isinstance(arr[0], list):
return flattenRecursive(arr[0]) + flattenRecursive(arr[1:])
return arr[:1] + flattenRecursiveweb(arr[1:])
我的想法如下:
功能 1 复杂性
我认为迭代版本的时间复杂度是O(n * m),其中n是初始数组的长度,m是嵌套的数量。我认为O(n) 的空间复杂度n 是初始数组的长度。
函数 2 复杂度
我认为递归版本的时间复杂度为O(n),其中n 是输入数组的长度。我认为O(n * m) 的空间复杂度,其中n 是初始数组的长度,m 是嵌套的深度。
总结
所以,对我来说,迭代函数似乎更慢,但空间效率更高。相反,递归函数更快,但空间效率较低。这是正确的吗?
【问题讨论】:
-
最终的扁平化列表的长度将是
O(n*m),对吧?因此,任何返回列表(而不是惰性迭代器)的算法几乎必须至少是O(n*m)空间。 -
此外,您似乎将诸如删除和插入列表中间、连接两个列表或复制列表尾部之类的事情计算为恒定时间步骤。但是其中每一个实际上都需要
O(p)为长度为 p 的列表工作。 -
顺便说一下,如果您知道如何编写
yield from flatten(elem)惰性递归版本,您可能想先尝试分析一下,因为它可能更容易处理——没有列表移位或连接操作,除了堆栈没有临时存储,只是计数O(1)步骤。 -
啊,我不知道
O(p)。你在说类似的东西:def iter_flatten(iterable): it = iter(iterable) for e in it: if isinstance(e, list): for f in iter_flatten(e): yield f else: yield e? -
如果 n 是初始列表长度,则不可能有
O(n)解决方案,考虑到[[[[[[[[[[0]]]]]]]]]]情况,其中 n 为 1,但最小可能步数为 9。我认为最好解决方案是O(n*m)(或者,如果您使用n作为最终列表大小而不是初始大小,则O(n+m))。我认为你可以使用iter_flatten来获得它,如果你使用像单链表而不是数组这样的常量可拼接的东西,你也可以使用flattenIterable来获得它。但如果不考虑更多,我不确定。