为什么在二进制搜索树中查找是O(log(n))?

时间:2013-01-20 16:52:29

标签: data-structures time-complexity big-o binary-search-tree

我可以看到,当在 BST 中查找值时,每次我们将节点与我们正在寻找的值进行比较时,我们会将树的一半留下。

但是我不明白时间复杂度为O(log(n))的原因。所以,我的问题是:

如果我们有一个N元素的树,为什么查找树并检查特定值是否存在的时间复杂度为O(log(n)),我们如何得到它?

5 个答案:

答案 0 :(得分:29)

您的问题似乎得到了很好的回答here,但总结一下您的具体问题,可能会更好地反过来考虑它; “随着节点数量的增加,BST解决方案的时间会发生什么变化?”

基本上,在BST中,每次将节点数量增加一倍,您只需将解决步骤数增加一个。为了扩展这一点,节点的四倍给出了两个额外的步骤。八次节点提供三个额外步骤。节点的十六次提供了四个额外的步骤。等等。

这些对中第一个数字的基数2对数是这些对中的第二个数字。它是基数为2的日志,因为这是一个二进制搜索(每一步都将问题空间减半)。

答案 1 :(得分:4)

对我来说,最简单的方法是查看log2(n)的图形,其中n是二叉树中的节点数。如表格所示:

          log2(n) = d

          log2(1) = 0
          log2(2) = 1 
          log2(4) = 2
          log2(8) = 3
          log2(16)= 4 
          log2(32)= 5
          log2(64)= 6             

然后我画一个小二叉树,这个从深度d = 0到d = 3:

            d=0          O
                        / \
            d=1        R   B
                      /\   /\
            d=2      R  B R  B
                    /\ /\ /\ /\
            d=3    R B RB RB R B

因此,当树中的节点数n有效加倍时(例如,当深度d从何时开始,n从7增加到15(几乎是倍增)时n增加8) d = 2到d = 3,增加1.)因此,所需的额外处理量(或所需时间)仅增加 1 额外计算(或迭代),因为处理量是相关的到d。

我们可以看到,从d = 2到d = 3,我们只下降了1个额外的深度d,以便在节点数量加倍后找到所有节点n中的节点。这是真的,因为我们现在搜索整棵树,以及我们需要搜索的一半以找到我们想要的节点。

我们可以将其写为d = log2(n),其中d告诉我们当树中有n个节点时,我们需要做多少计算(平均多少次迭代)才能到达树中的任何节点

答案 2 :(得分:3)

这可以非常容易地以数学方式显示。

在我提出这个问题之前,让我澄清一下。在平衡二叉搜索树中查找或查找的复杂性为O(log(n))。对于二进制搜索树,通常是O(n)。我将在下面展示。

在平衡的二叉搜索树中,在最坏的情况下,我要查找的值位于树的叶子中。我基本上会从根到叶子遍历,只看一次树的每一层 - 因为BST的有序结构。因此,我需要做的搜索次数是树的层数。因此,问题归结为找到具有n个节点的树的层数的闭合表达式。

这是我们进行简单归纳的地方。只有1层的树只有1个节点。 2层树具有1 + 2个节点。 3层1 + 2 + 4个节点等。图案清晰:具有k层的树正好

N = 2 ^ 0 + 2 ^ 1 + ... + 2 ^ {K-1}

节点。这是一个几何系列,暗示

N = 2 ^ k-1个,

等效:

k = log(n + 1)

我们知道大哦对n的大值感兴趣,因此常数是无关紧要的。因此O(log(n))复杂度。

我会给另一个 - 更短的方式来显示相同​​的结果。因为在寻找值时我们不断地将树分成两半,我们必须这样做k次,其中k是层数,以下是真的:

(n + 1)/ 2 ^ k = 1,

意味着完全相同的结果。你必须说服自己n + 1中的+1来自哪里,但即使你不注意它也没关系,因为我们讨论的是n的大值。

现在让我们讨论一般的二叉搜索树。在最坏的情况下,它是完全不平衡的,意味着它的所有节点只有一个子节点(并且它成为一个链表)。 https://www.cs.auckland.ac.nz/~jmor159/PLDS210/niemann/s_fig33.gif

在这种情况下,为了在叶子中找到值,我需要迭代所有节点,因此O(n)。

最后要注意的是,这些复杂性不仅适用于查找,还适用于插入和删除操作。

(当我达到10个重复点时,我会用更好看的乳胶数学样式编辑我的方程式。所以现在不会让我这么做。)

答案 3 :(得分:1)

每当您看到其中包含O(log n)因子的运行时,there's a very good chance that you're looking at something of the form "keep dividing the size of some object by a constant."因此,考虑这个问题的最佳方法可能是 - 因为您在二叉搜索树中进行查找究竟是什么因为一个常数因素而被削减了,那究竟是什么常数呢?

首先,让我们想象你有一个完美平衡的二叉树,看起来像这样:

                     *
              /             \
             *               *
          /     \         /     \
         *       *       *       *
        / \     / \     / \     / \
       *   *   *   *   *   *   *   *

在执行搜索的每个点,您都会查看当前节点。如果它是您正在寻找的那个,太好了!你完成了。另一方面,如果它不是,那么你要么下降到左子树或右子树,然后重复这个过程。

如果你走进两个子树中的一个,你实际上是在说"我根本不关心那个其他子树中的内容。"你将其中的所有节点都扔掉了。那里有多少个节点?好吧,通过快速的视觉检查 - 最好是一个跟着一些不错的数学运算 - 你会发现你正在扔掉树中大约一半的节点。

这意味着在查找的每个步骤中,您要么(1)找到您要查找的节点,要么(2)抛出树中一半的节点。由于你在每一步都做了一定量的工作,你就会看到O(log n)行为的标志性行为 - 工作在每一步下降一个常数因子,所以它只能以对数方式多次这样做。

当然,并非所有树木都是这样的。 AVL树有一个有趣的属性,每次你下降到一个子树,你扔掉大约总节点的黄金比例分数。因此,这可以保证在节点用完之前只能以对数方式进行许多步骤 - 因此O(log n)高度。在一个红色/黑色的树中,每个步骤都会丢弃(大约)总节点的四分之一,并且由于你以一个常数因子缩小,你再次得到你想要的O(log n)查找时间。非常有趣的替罪羊树有一个可调参数,用于确定它的平衡程度,但是你可以再次证明你所采取的每一步都会根据这个可调参数抛弃一些常数因子,给出O(log n)查找。

然而,这种分析打破了不平衡的树木。如果你有一个纯粹的退化树 - 每个节点只有一个孩子的树 - 那么你所采取的树的每一步只会抛弃一个节点,而不是一个恒定的分数。这意味着在最坏的情况下查找时间会达到O(n),因为从n中减去常量的次数是O(n)。

答案 4 :(得分:1)

  

如果我们有一棵由N个元素组成的树,为什么查找时间复杂   树并检查是否存在特定值是O(log(n)),该怎么办   我们明白了吗?

那不是真的。默认情况下,二进制搜索树中的查找不是O(log(n)),其中n是多个节点。在最坏的情况下,它可能变成O(n)。例如,如果我们插入以下序列n, n - 1, ..., 1的值(以相同顺序),则树将如下所示:

                  n
                 /
              n - 1
               /
            n - 2
             /
           ...
           1

对值为1的节点进行查找的时间复杂度为O(n)

要使查找更有效率,必须平衡树,使其最大高度与log(n)成比例。在这种情况下,查找的时间复杂度为O(log(n)),因为查找任何叶都受log(n)操作的限制。

但同样,并非每一个二进制搜索树都是平衡二进制搜索树。您必须进行权衡以保证O(log(n))的时间复杂度。