【问题标题】:Why does this binary tree search take so much longer than an insertion?为什么这种二叉树搜索比插入花费的时间要长得多?
【发布时间】:2017-06-03 04:01:50
【问题描述】:

我正在尝试学习/理解一些基本算法,今天我决定用 Go 编写一棵二叉树。这是结构的样子:

type Node struct {
  Value int
  Left  *Node
  Right *Node
}

这是我检查树是否包含 int 的函数:

func (tree *Node) Contains(val int) bool {
  if val == tree.Value {
    return true
  } else if val > tree.Value {
    if tree.Right != nil {
      return tree.Right.Contains(val)
    } else {
      return false
    }
  } else if val < tree.Value {
    if tree.Left != nil {
      return tree.Left.Contains(val)
    } else {
      return false
    }
  } else { // huh
    return false
  }
}

我编写了一个测试函数来测试树上的不同操作需要多长时间。我的 Insert() 函数 34ms 需要插入 100,000 个随机整数,我的 Contains() 函数 33ms 需要检查树是否包含 100,000 个随机整数。如果我将随机整数的数量增加到 1,000,000,我的 Insert() 函数需要 34ms 才能运行,但我的 Contains() 函数突然需要 321ms 才能运行。

为什么Contains() 的运行时间大幅增加,而Insert() 几乎保持不变?

【问题讨论】:

  • insert 函数在哪里?
  • 您能展示一下您正在使用的 Benchmark 函数吗?
  • 你应该在计时之前完成运行 5-10 次,有时诸如 java (JIT) 之类的语言会在运行时进行优化,不确定 go,但是微基准测试很棘手。
  • 二叉树不是二叉搜索树
  • 根据您的描述,我怀疑树实际上只包含 100,000 个整数,因此插入所需的时间相同。但是您的查询次数是原来的 10 倍。

标签: algorithm go time-complexity binary-tree


【解决方案1】:

Insert 函数应该定期重新平衡树,因为不平衡的树可能会导致非常不均匀的遍历时间。因此,Insert应该通常比Contains慢。

如果您的 Insert 函数没有重新平衡树,那么任何给定函数所需的时间将变为 O(n) 最坏情况而不是 O(log n) 并且相当不可预测。

此外,在谈论 O(...) 时间复杂度时,我们通常谈论的是最坏情况行为。如果您对单个调用进行计时,那么任何给定调用所花费的时间都可能比最坏的情况要少得多——例如,Contains 寻找恰好是根的节点将立即返回,而不管大小如何。

【讨论】:

  • 在这种情况下,Insert 也应该变成O(n),不是吗?
  • @user58697:最坏的情况,是的,但是任何给定的调用都可能以不可预知的方式花费更少的时间。
猜你喜欢
  • 2018-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多