迭代和递归算法的时间复杂度

时间:2015-09-12 12:51:40

标签: algorithm time-complexity

我有理解算法时间复杂度的问题。

让我们的第一个例子采用这种算法在二叉搜索树中搜索:

def search_iteratively(key, node): 
     current_node = node
     while current_node is not None:
         if key == current_node.key:
             return current_node
         elif key < current_node.key:
             current_node = current_node.left
         else:  # key > current_node.key:
             current_node = current_node.right
     return None

那么,如何计算这个时间复杂度呢?

让我们以这种递归算法为例:

int f(int a, int b) 
{ 
    if (a > 0)
        return f(a − 1, b − 3); 
    else 
        return b;
}

所以,我认为这个算法的时间复杂度是O(a),因为结束条件只取决于a参数。

如果我写下来:

T(a, b) = O(1) where a <= 0
T(a, b) = T(a-1, b-3) where a > 0

T(a, b) = 
T(a-1, b-3) = 
T(a-1, b-3) + T(a-2, b-6) = 
T(a-1, b-3) + T(a-2, b-6) + T(a-3, b-9)

那么,我怎么知道这是线性时间复杂度?只是因为当a小于1时递归将结束?

最后:

  • 我们可以将每个递归算法转换为 迭代?
  • 是递归算法的速度通常是否缓慢 而不是迭代?
  • 我们可以用循环替换尾递归吗?

3 个答案:

答案 0 :(得分:1)

至于树搜索算法的复杂性 - 尝试考虑每次迭代时都会发生变化的事情。提示:考虑树中current_node的深度。

尝试使用归纳来证明这种特殊情况下的线性复杂性。你知道T(0,x)将以单个调用结束,这将是你的基础。尝试证明T(n,x)将执行n次递归调用。

  • 有一个定理可以将每个迭代算法转换为递归,反之亦然
  • 如果你实现相同的算法没有任何递归优化,迭代递归将会更慢,因为有函数调用 - 必须在堆栈上分配一个新的框架,然后必须弹出它
  • 在大多数情况下用循环替换尾递归相对容易

答案 1 :(得分:1)

在二叉搜索树中查找值的最坏情况时间复杂度是多少?最糟糕的情况是你必须下降到最深的叶子。通常,n个节点的二叉树可以具有深度O(n)。 (想想一个案例,每个孩子都是一片叶子,而左边的孩子往下行。)但是,如果你保持一个平衡的二元搜索树,例如red-black tree,你的高度为{{1} }。这是红黑树中键查找操作的最坏情况运行时间。

您的函数O(log n)定义为:

  • f if f(a, b) = f(a − 1, b − 3)
  • a > 0否则

我们可以通过f(a, b) = b上的归纳证明,评估a f(a, b)的任何非负值都需要a af次呼叫。在基本情况下,a == 0只调用f一次。对于正数a,假设f(a - 1, b)被称为a - 1次。然后,评估f(a, b)需要a - 1 + 1 = a次来电f。 (顺便说一句,我们可以观察到f(a, b) = b - 3*a并实现恒定时间。)

每个递归算法都可以转换为迭代算法,该算法模拟执行递归函数调用的堆栈。观察计算机执行迭代以实现递归程序。更深刻的是,图灵机是迭代的。计算机科学的一个公理是可以用图灵机计算所有可以计算的东西。 lambda演算提供的计算能力不比图灵机那么大。

递归算法通常比迭代算法占用更多的时间和空间,因为它们需要为每个函数调用在堆栈上分配新的帧。

如果以每个函数调用处于尾部位置的方式编写递归函数,意味着调用不返回需要进一步计算的中间值,则它是尾递归函数。递归计算不依赖于递归调用的参数以外的任何值。因此,对函数的最终调用会立即生成最终结果,并且无需返回递归调用链。

编译器可以实现尾递归,以便重用当前帧,而不必在堆栈上分配新帧。例如,方案编译器需要这样做。由此产生的计算具有迭代的性能特征,但代码具有递归的表现优势。

答案 2 :(得分:1)

二叉树算法最多访问树中的每个节点一次;否则二叉树中有一个循环,这是一个矛盾。此外,在最坏的情况下,它至少访问每个节点一次。因此,对节点的访问次数是Theta(n),并且由于每次访问需要Theta(1)时间,因此最坏情况下的运行时间是Theta(n)。

我们&#34;知道的方式&#34;复发的解决方案是通过归纳证明。在您的情况下,基础是

任何b的T(0,b)= c

对于任何b,诱导接触是T(n,b)<= c(n + 1)。

诱导步骤

T(n,b)&lt; = c + T(n-1,b-3)&lt; = c + cn = c(n + 1)。

由此得出T(n)= O(n)。

不,递归算法不一定慢,是的,循环可以被尾递归替换。

相关问题