Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/311.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 递归算法的运行时复杂性_Java_Recursion_Big O_Time Complexity_Tailrecursion Modulo Cons - Fatal编程技术网

Java 递归算法的运行时复杂性

Java 递归算法的运行时复杂性,java,recursion,big-o,time-complexity,tailrecursion-modulo-cons,Java,Recursion,Big O,Time Complexity,Tailrecursion Modulo Cons,我到处搜索,似乎找不到很多与运行时复杂性、递归和java相关的资料 我目前正在算法类中学习运行时复杂性和大O表示法,并且在分析递归算法时遇到了困难 private String toStringRec(DNode d) { if (d == trailer) return ""; else return d.getElement() + toStringRec(d.getNext()); } 这是一种递归方法,它将简单地遍历双链表并打印出元素 我唯一能想到的

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

我目前正在算法类中学习运行时复杂性和大O表示法,并且在分析递归算法时遇到了困难

private String toStringRec(DNode d)
{
   if (d == trailer)
      return "";
   else
      return d.getElement() + toStringRec(d.getNext());
}
这是一种递归方法,它将简单地遍历双链表并打印出元素

我唯一能想到的是,它的运行时复杂性为O(n),因为递归方法调用的数量将取决于数据列表中的节点数量,但我仍然对这个答案感到不舒服

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


或者我完全偏离了轨道,运行时复杂性是恒定的,因为它所做的一切都是从
DList
中的
DNodes
检索元素。

正如您所建议的,该算法的运行时复杂性为O(n)。您的列表中有n个项,算法将为每个项执行几乎固定的工作量(该工作量为元素和下一次访问,加上一个新的toString调用)。从DNode检索元素需要恒定的时间,而恒定的时间在big-O表示法中被丢弃


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

乍一看,这看起来像是尾调用的一个典型例子。它相当于具有迭代次数的循环

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

如果T(N)是基本操作的数量(在本例中,当我们进入函数体时,函数中的任何一行最多执行一次,除了第二次返回之外,其他所有返回都不是O(1))通过对N个元素的列表调用toString执行,那么

  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)。

这是一个非常简单的例子,但诀窍是定义一个递归关系,它是给定输入大小的运行时间在较小输入大小方面的函数。对于本例,假设每一步所做的功取恒定时间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的运行时或者其他分治算法,你可以更好地了解递归关系。

对于这种递归算法,通常可以编写一个递归方程来计算顺序。通常用T(n)表示执行的指令数。在本例中,我们有:

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

(假设函数
getElement
在恒定时间内运行。)该方程的解为T(n)=O(n)


这是一般情况。但是,有时您可以分析算法,而不必编写这样的公式。在本例中,您可以很容易地辩称,每个元素最多访问一次,并且每次完成一些固定时间的工作;所以,完成这项工作需要O(n)个整体。

Hmm,我认为d.getElement()是为了获取存储在节点d上的数据。我想他需要把他的问题说清楚一点…@XiaoChuanYu No,
d.getElement()
O(1)
。接下来的字符串连接是线性的。是的,感谢您没有忽略字符串连接的成本。这是完全正确的。高斯和
1+2+…+n
开始起作用,这就是二次函数的来源。当然,在这个例子中,你也可以用常识来理解它:你正在打印链接列表中的每个节点,因此你执行的打印数的增长速度与列表的大小完全相同。所以这是线性时间。在这个特定的例子中,我们不能假设每一步所做的工作都是常数时间,因为Java中的字符串串联是如何工作的。我认为做这个假设是可以的,因为这个问题的重点不是查查Java库函数的复杂性,但是为了理解这种递归算法是如何在一般情况下进行分析的,我同意公式化递归是解决这个问题的关键。但是:我们需要确保我们解决了正确的问题。如果我们实际运行这个程序并在n范围内绘制它的行为,我们将观察到O(n^2)时间。这需要解释,否则我们的分析就没用了。必须将递归修改为
T(n)=C*n+T(n-1)
,因为字符串连接的基本操作在被连接字符串的大小上是线性的。除非语言提供了字符串绳索,否则我们必须处理字符串上的
+
的非恒定成本。是的,这很公平。谢谢你给我介绍绳子,我以前没听说过。不幸的是,这个解释提供了一个错误的结论。它没有考虑字符串连接的成本。也就是说,每个项目的成本不是恒定的。这是这个问题的一个要点。请更正。我同意每个项目的成本不是恒定的,但我不同意它是O(n)。
string1+string2的成本是O(m),其中m是结果字符串的长度。具体来说,连接两个字符串最糟糕的情况是创建长度为m的新字符[],并从原始字符串一次复制每个字符。在t上时