求和函数的递归实现的时空复杂度

时间:2020-04-08 21:43:44

标签: python performance recursion time-complexity space-complexity

谁能建议以下代码的时空复杂性? 我知道时间复杂度应为O(n),因为该函数被调用了n次,并且空间复杂度至少为O(n)(由于堆栈空间),但是确实将a [1:]传递给函数会导致空间复杂度增加了吗?我认为a [1:]将在省略第一个元素的同时创建 a 的新副本,对吗?

def sum(a):
    if len(a) == 1:
        return a[0]
    return a[0] + sum(a[1:])

2 个答案:

答案 0 :(得分:1)

时间复杂度类似于theta(n ^ 2),因为每次执行a [i:]时,您基本上都会将列表从i复制到末尾,因此必须对其进行迭代。至于空间的复杂性,应用程序堆栈将包含所有要调用的列表,首先是包含n个元素的列表,然后是n-1,依此类推,直到1,然后开始清空堆栈。因此,您最终也将面临theta(n ^ 2)的复杂性。

答案 1 :(得分:0)

作为递归函数,如果不应用任何tail-call优化,那么在这种情况下,考虑到它在内存堆栈上的执行,它的空间复杂度肯定至少为O(n)。但是让我们进一步分析:

时间复杂度

我们知道sum是递归的,其终止条件是输入数组为单一长度时。因此我们知道,考虑到大小为Sum的输入数组,在最坏的情况下O(n)将至少被调用n次。考虑一下递归的含义,即。例如,循环

但是,在函数内,我们有切片操作。切片操作l[a:b]O(b-a),因此此操作在第一次运行中的复杂度为O(n-1),在第二次运行中的复杂度为O(n-2),依此类推。我们最初认为,它将执行整个数组的副本。此函数的整体时间复杂度应为O(n^2),因为它会以大小为n的数组为每个项目创建一个切片。

空间复杂度

现在谈论内存空间。

len(a) == 1

在这里,我们从len(a)的返回值中获得了一个副本。

return a[0]

    return a[0] + sum(a[1:])

在以上两行中,我们将有一个值的另一个副本,该副本将存储在函数的返回地址中。该切片还具有O(n)的空间复杂度。

看到这一点,并考虑到编译器未应用重大突破的优化,例如reduction,我们说此函数的空间复杂度为O(n),因为它将使<每个输入的“ strong>恒定个副本数,并且将以大小为n的数组执行切片操作。

由于我们从一开始就说过递归就像一个循环,因此考虑到没有尾调用优化,在最坏的情况下,整个功能将执行n次。该程序将增加函数的内存堆栈,直到达到停止条件为止,直到最终可以从调用堆栈中“弹出”返回值为止。因此,总空间复杂度也为O(n*log n)(因为每次执行时输入数组较小)。

Ps:

根据this,我还认为len(a)的时间复杂度为O(1)