我对运行时的递归模式有疑问。
示例1
int f(int n) {
if(n <= 1) {
return 1;
}
return f(n - 1) + f(n - 1);
}
我可以理解上面代码的运行时间是O(2 ^ N),因为如果我传递5,它会调用4两次,然后每次调用3次,然后跟踪直到达到1,即类似O(分支^深度)。
示例2 平衡二进制树
int sum(Node node) {
if(node == null) {
return 0;
}
return sum(node.left) + node.value + sum(node.right);
}
我读到上面代码的运行时是O(2 ^ log N),因为它是平衡的但我仍然把它看作O(2 ^ N)。谁能解释一下呢?
修改 我们可以求解O(2 ^ log N)= O(N)但我看到它是O(2 ^ N)。
谢谢!
答案 0 :(得分:3)
二叉树的复杂性O(n)
与此处的任何其他树一样,因为您最终遍历树的所有元素。通过减半,除了分别计算相应孩子的总和之外,我们没有做任何特殊的事情。
这个术语是这样的,因为如果它是平衡的,那么2^(log_2(n))
是树中元素的数量(叶子+非叶子)。(log2(n)
级别)
如果它不平衡,那也无关紧要。我们正在进行一项操作,需要考虑每个元素使运行时为O(n)
。
哪里可能有重要意义?如果它正在搜索一个元素,那么它将是重要的(无论它是否平衡)。
答案 1 :(得分:1)
我会捅这个。
在平衡二叉树中,您应该在每个父节点的左侧和右侧有一半的子节点。树的第一层是根,有1个元素,然后是下一层中的2个元素,然后是下一个中的4个元素,然后是8,依此类推。因此,对于具有L层的树,树中有2^L - 1
个节点。
反过来说,如果要插入树中有N个元素,最后会得到一个深度为L = log_2(N)
的平衡二叉树,所以你只需要调用你的递归算法对于log_2(N)
层。在每一层,您的算法调用次数都会增加一倍,因此在您的情况下,最终会有2^log_2(N)
次调用和O(2^log_2(N))
运行时间。请注意2^log_2(N) = N
,所以它们都是相同的,但我们会在一秒钟内获得二叉树的优势。
如果树不平衡,最终深度大于log_2(N)
,因此您有更多的递归调用。在极端情况下,当您的所有孩子都在父母的左侧(或右侧)时,您有N个递归调用,但每个调用都会立即从其中一个分支返回(一侧没有孩子)。因此,您将拥有O(N)
运行时间,这与以前相同。每个节点都被访问过一次。
平衡树的优势在于搜索等情况。如果左手孩子总是小于父母,而右手孩子总是大于,那么你可以在n
时间内在N
个节点中搜索元素O(log_2(N))
(不是2^log_2(N)
!)。但是,如果您的树严重失衡,则此搜索将成为所有值的线性遍历,您的搜索结果为O(N)
。如果N
非常大,或者您执行此搜索,那么这可能是易处理算法和难处理算法之间的差异。