【问题标题】:Count number of nodes within range inside Binary Search Tree in O(LogN)在 O(LogN) 中计算二叉搜索树范围内的节点数
【发布时间】:2016-05-03 16:56:39
【问题描述】:

给定一个 BST 和两个整数 'a' 和 'b' (a

我知道在 LogN 时间内可以很容易地找到 a 和 b 的位置,但是如何在不进行遍历的情况下计算其间的节点,即 O(n)?

【问题讨论】:

  • 为什么不干脆让每个节点维护一个变量,下面的子树中的节点数呢?
  • 你不能在 LogN 时间内计算 N 个东西——你必须在构建树时就已经创建了信息。
  • 增强搜索树?

标签: algorithm data-structures binary-tree


【解决方案1】:

在二叉搜索树的每个节点中,还要计算树中小于其值的值的数量(或者,对于下面脚注中提到的不同树设计,其左子树中的节点) .

现在,首先找到包含值a 的节点。获取已存储在此节点中的小于a 的值的计数。这一步是Log(n)。

现在找到包含值b 的节点。获取存储在此节点中的小于b 的值的计数。这一步也是Log(n)。

减去这两个计数,您就得到了ab 之间的节点数。此搜索的总复杂度为 2*Log(n) = O(Log(n))。


this video。教授在这里使用 Splay Trees 解释您的问题。

【讨论】:

  • “Splay Trees 中的节点根据设计自动保持左右子树中元素的计数”这对我来说是个新闻……splay trees 的常见公式没有那个属性 AFAIK
  • @NiklasB.:我从 here 学习了 Splay Trees。这就是他描述张开树的方式。我同意维基百科页面没有给出这个定义。
  • @NiklasB.:为了保持答案的普遍性和正确性,我也更新了它。
  • 你能给我时间吗?基本上,您现在的回答建议使用带有子树大小增加的 splay 树,但显然您拥有您所说的可以避免的簿记信息?
  • @NiklasB.:他不仅这样描述 Splay Tree,他甚至还解释了如何使用 Splay Tree 的这些属性来查找某些 ab.
【解决方案2】:

简单的解决方案:

  • 从根节点开始检查
  • 如果Node在范围内,则将其加1并递归签入左右子节点
  • 如果节点不在范围内,则检查范围内的值。如果范围值小于根,那么绝对可能的情况是左子树。否则检查右子树

    这里是示例代码。希望它清除。

    if (node == null) {
        return 0;
    } else if (node.data == x && node.data == y) {
        return 1;
    } else if (node.data >= x && node.data <= y) {
        return 1 + nodesWithInRange(node.left, x, y) + nodesWithInRange(node.right, x, y);
    } else if (node.data > x && node.data > y) {
        return nodesWithInRange(node.left, x, y);
    } else {
        return nodesWithInRange(node.right, x, y);
    }
    

时间复杂度:- O(logn)+ O(K)

K 是 x 和 y 之间的元素数。

这不是很理想,但在您不想修改二叉树节点定义的情况下很好。

【讨论】:

  • 不是一个解决方案(还)。如果一个节点在范围之外,则返回 0,忽略该范围属于其左子树或右子树。一旦你适应了这一点,你的答案类似于FallAndLearn's answer,包括挥手这是in O(log n)
  • 嘿,谢谢。好点子 !我已经更新了我的解决方案。还要说明复杂性细节。
【解决方案3】:

将BST的中序遍历存储在数组中(它将被排序)。搜索“a”和“b”将花费 log(n) 时间并获取它们的索引并获取差异。这将给出“a”到“b”范围内的节点数。

空间复杂度 O(n)

【讨论】:

  • 中序遍历将花费 O(n) 时间。你不算数。
  • 是的!这将构建一次,但查询结果将始终为 O(log n)
【解决方案4】:

想法很简单。

  1. 从根开始遍历 BST。
  2. 检查每个节点是否在范围内。 如果它在范围内,则计数++。并为它的两个孩子复发。
  3. 如果当前节点小于范围的下限,则为右孩子递归,否则为左孩子递归。

时间复杂度为O(height + number of nodes in range)..

对于您的问题,为什么不是O(n)

因为我们没有遍历整棵树,也就是树中的节点数。我们只是根据父节点的数据遍历需要的子树。

伪代码

int findCountInRange(Node root, int a, int b){

    if(root==null)
       return 0;
    if(root->data <= a && root->data >= b)
         return 1 + findCountInRange(root->left, a, b)+findCountInRange(root->right, a, b); 
    else if(root->data < low)
         return findCountInRange(root->right, a, b);
    else 
         return findCountInRange(root->left, a, b);

}

【讨论】:

  • 如果查询范围覆盖了树中的所有元素怎么办?那么你的算法确实是 Omega(n)
  • 更正。复杂度将是 O(高度 + 范围内的节点数)。
  • 如果 a 和 b 分别是树中的最低和最高节点怎么办?我是否遗漏了一些可以探索这些节点 O(nlog) 的数学属性?
  • 如果 a 和 b 是最低和最高节点。那么时间复杂度就是O(heigh+n),可以写成O(n)。
  • 不是我 :),但我认为是因为您的解决方案给出了 O(n) 而不是 O(logN)。
猜你喜欢
  • 2020-08-25
  • 1970-01-01
  • 2020-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-07
  • 1970-01-01
相关资源
最近更新 更多