理解大O符号 - 破解编码访谈示例9

时间:2017-07-21 04:19:04

标签: algorithm tree big-o notation

我对这两个代码感到困惑。

代码1

int f(int n){
    if (n <= 1){
         return 1;
    }
    return f(n-1) + f(n-1);
}

代码2 (平衡二叉搜索树)

int sum(Node node){
    if(node == null){
        return 0;
    }
    return sum(node.left) + node.value + sum(node.right);
}

作者说代码1的运行时为O(2 ^ n),空间复杂度为O(n)

代码2是O(N)

我不知道这两个代码之间有什么不同。看起来两者都是相同的二叉树

6 个答案:

答案 0 :(得分:5)

首先,了解两种情况下的N是很重要的。 在第一个例子中,它很明显,因为你直接在代码中看到它。对于第一种情况,如果您构建f(i)次调用树,则会看到它包含O(2^N)个元素。的确,

            f(N)             // 1 node
        /          \ 
   f(N-1)         f(N-1)     // 2 nodes
  /      \      /      \
f(N-2) f(N-2)  f(N-2) f(N-2) // 2^2 nodes
...
f(1) ........ f(1)           // 2^(N-1) nodes

在第二种情况下,N(很可能)是树中的许多元素。正如您可能从代码中看到的那样,我们只遍历每个元素一次 - 您可能会意识到,每个树节点都会调用node.value一次。因此O(N)。

请注意,在此类任务中, N 通常表示输入的大小,而输入取决于您的问题。它可以只是一个数字(比如你​​的第一个问题),一维数组,二叉树(比如你的第二个问题),甚至是一个矩阵(尽管在后一种情况下你可能会看到像&这样的显式语句#34;大小为M * N&#34;的矩阵。

所以你的困惑可能来自N&#34;的定义。这两个问题有所不同。换句话说,我可能会说n2 = 2^n1

答案 1 :(得分:4)

那是错误的,因为第一个片段在O(2 ^ n)中运行而不是O(n ^ 2)。

解释是: 在每个步骤中,我们递减n但是创建两次调用次数,因此对于n,我们用f(n-1)调用两次,并且对于n-1的每一次调用,我们将调用n = 1。用f(n-2)呼叫两次 - 这是4次呼叫,如果我们再次降低级别,我们用f(n-3)呼叫8次:所以呼叫次数为: 2 ^ 1,然后是2 ^ 2,然后是2 ^ 3,2 ^ 4,...,2 ^ n。

第二个片段在二叉树上进行一次传递,并且恰好到达每个节点一次,所以它是O(n)。

答案 2 :(得分:1)

第一个代码确实是O(2^n)

但第二个代码不能是O(n),因为那里没有n。这是许多人忘记的事情,通常他们假设 n是什么而不澄清它。

事实上,您可以根据任何事情估算任何事物的增长速度。有时它的输入大小(在第一个代码中是O(1)O(log n)取决于大数字的用法),有时只是在参数上,如果它是数字的。

因此,当我们开始考虑第二个代码中的时间和内存依赖时,我们可以得到这些东西:

  1. time=O(number_of_nodes_in_tree)
  2. time=O(2^height_of_tree)
  3. additional_space=O(height_of_tree)
  4. additional_space=O(log(number_of_nodes))(如果树是平衡的)
  5. 所有这些都是同时正确的 - 它们只是将某些东西与不同的东西联系起来。

答案 3 :(得分:0)

代码1:

if()语句根据传入参数的内容运行n次,但函数调用自身n-1次。简化:

n * (n-1) = n^2 - n = O(n^2 - n) = O(n^2)

代码2:

搜索只遍历树的每个元素一次,函数本身没有任何for()。由于有n个项目,并且只访问过一次,因此它是O(n)

答案 4 :(得分:0)

对于代码2,要确定函数的大O,我们是否必须考虑重复的成本以及重复运行的次数?

如果我们使用两种方法来估计使用递归树和主定理的Big O:

递归树: 每个级别的总成本对于每个级别将是cn,因为递归调用的数量和输入的分数是相等的,并且树的级别是lg(n),因为它是平衡的二叉搜索树。那么运行时间应该是nlg(n)?

主定理: 这应该是情况2,因为f(n)= n ^ logbase a(b)。那么根据主定理,它应该是nlg(n)运行时间?

答案 5 :(得分:0)

您对这两种情况的“ N”感到困惑。在第一种情况下,N表示给定的输入。因此,例如,如果N = 4,则调用的函数数为2 ^ 4 = 16。您可以绘制递归图进行说明。因此,O(2 ^ N)。

在第二种情况下,N表示二叉树中的节点数。因此,此N与输入无关,但与二叉树中已经存在的节点数无关。因此,当用户调用该函数时,它会访问每个节点一次。因此,O(N)。