递归算法的运行时复杂性

时间:2012-03-02 21:34:52

标签: java recursion big-o time-complexity tailrecursion-modulo-cons

我搜索过高和低,似乎找不到很多与运行时复杂性,递归和java相关的资料。

我目前正在学习Algorithms类中的运行时复杂性和Big-O表示法,而且我在分析递归算法时遇到了麻烦。

private String toStringRec(DNode d)
{
   if (d == trailer)
      return "";
   else
      return d.getElement() + toStringRec(d.getNext());
}

这是一个递归方法,它将简单地迭代一个双向链表并打印出元素。

我唯一能想到的是它的运行时复杂度为O(n),因为递归方法调用的数量将取决于DList中的节点数,但我仍然没有对这个答案感到满意。

我不确定是否应该考虑添加dd.getNext()

或者我完全偏离轨道并且运行时复杂性是不变的,因为它所做的只是从DNodes中的DList检索元素?

5 个答案:

答案 0 :(得分:3)

乍一看,这看起来像tail recursion modulo cons的经典案例,是尾调的概括。它等效于具有迭代次数的循环。

然而,事情并非如此简单:这里的棘手问题是将d.getElement()添加到不断增长的字符串中:这本身就是线性操作,并且重复N 1}}次。因此,函数的复杂性为O(N^2)

答案 1 :(得分:2)

如果T(n)是基本操作的数量(在这种情况下 - 当我们进入函数体时,内部的任何行最多执行一次,除第二次返回之外的所有行都不是O(1) )通过在n个元素的列表上调用toStringRec来执行,然后

  T(0) = 1  - as the only things that happen is the branch instruction and a
              return
  T(n) = n + T(n-1) for n > 0 - as the stuff which is being done in the
              function besides calling toStringRec is some constant time stuff and
              concatenating strings that takes O(n) time; and we also run
              toStringRec(d.getNet()) which takes T(n-1) time

此时我们已经描述了算法的复杂性。我们现在可以计算T的闭合形式,T(n)= O(n ** 2)。

答案 2 :(得分:2)

这是一个非常简单的例子,但诀窍是定义一个递归关系,它是较小输入大小的给定输入大小的运行时的函数。对于这个例子,假设在每个步骤完成的工作需要恒定的时间C并假设基本情况不起作用,那就是:

T(0) = 0
T(n) = C + T(n-1)

然后,您可以使用替换来解决运行时间以查找系列:

T(n) = C + T(n-1) = 2C + T(n-2) = 3C + T(n-3) = ... = nC + T(n-n) = nC + 0 = nC

根据O的定义,该等式为O(n)。这个例子并不是特别有趣,但如果你看一下类似mergesort的运行时或其他分而治之的算法,你可以更好地了解递归关系。

答案 3 :(得分:0)

该算法的运行时复杂度为O(n),如您所示。您的列表中包含n个项目,算法将为每个项目执行近乎固定的工作量(该工作是元素和下一个访问,以及新的toStringRec调用)。从DNode中检索元素需要恒定的时间,并且以big-O表示法丢弃常量时间。

关于递归方法(在大多数情况下)的有趣之处在于它们在空间复杂度方面也是O(n)。为每次调用toStringRec创建一个新的堆栈条目(用于存储传递给该方法的参数),该调用被称为n次。

答案 4 :(得分:0)

对于这样的递归算法,通常可以编写递归方程来计算顺序。习惯上显示用T(n)执行的指令数量。在这个例子中,我们有:

T(n)= T(n-1)+ O(1)

(假设函数getElement在恒定时间内运行。)这个等式的解决方案通常是T(n)= O(n)。

这是一般情况。但是,有时您可以在不编写此类公式的情况下分析算法。在这个例子中,您可以轻易地争辩说每个元素最多只能被访问一次,并且每次完成一些恒定时间的工作;所以,整体工作需要O(n)。