计算给定范围内AVL树中的节点数

时间:2015-06-14 21:06:56

标签: c++ algorithm tree

我需要编写一个C ++函数,给定范围(a,b),返回AVL树中属于该给定范围的节点数,特别是log(n)时间复杂度。 如果我需要,我可以在树的节点上添加更多字段。

我应该指出a,b不一定会出现在树上。例如,如果树的节点是:1,2,5,7,9,10,那么使用参数(3,9)运行函数应返回3.

我应该使用哪种算法来实现这一目标?

2 个答案:

答案 0 :(得分:2)

这是一个着名的问题 - dynamic order statistcs by tree augmentation

您基本上需要扩充节点,以便在查看子指针时,您知道在时间O(1)处子节点子树中有多少个子节点。很容易看出这可以在不影响复杂性的情况下完成。

一旦你有了这个,你可以通过从节点到根执行两次遍历来回答任何查询(在此和之间,包含/排除 - 所有可能性)。确切的遍历取决于细节(例如,检查C ++中的函数lower_boundupper_bound)。

答案 1 :(得分:1)

首先,您可以通过键操作实现拆分。也就是说,给定一棵树,执行split(tree, key, ts, tg)将键分成两棵树; ts包含小于key的密钥; t2更大或更平等的。该操作可以在O(lg n)中完成。

然后,通过两个分割,第一个在a上,第二个在b上,你可以在O(lg n)中获得所需的子集范围。

拆分可以按如下方式实现(伪代码):

void split(Node * root, const Key & key, Node *& ts, Node *& tg) noexcept
  {
    if (root == Node::NullPtr)
      return;

    if (key < KEY(root))
      {
         Node * r = RLINK(root), * tgaux = Node::NullPtr;
         split(LLINK(root), key, ts, tgaux);         
         insert(tgaux, root); // insert root in tgaux
         tg = join_ex(tgaux, r);
       }
    else 
      {   // ket greater or equal than key to tg
        Node * l = LLINK(root), *tsaux = Node::NullPtr;
        split(RLINK(root), key, tsaux, tg));
        insert(tsaux, root); // insert root in tsaux
        ts = join_ex(l, tsaux);
       }
  }

join_ex(t1, t2)加入两棵独树;也就是说,t1的所有键都比树t2的任何键都小。该连接可以用O(lg n)实现,其方式类似于Knuth在TAOCP V3 6.2.3中描述的连接。

Grosso modo 如果你想加入lr,那么假设h(l)&gt; H(R)。您从r最左边的节点(最小)中删除。让j加入节点r'生成的树(r - j)。现在,您向r的右侧下降,直到到达节点p,使h(p) - h(r')等于0或1.此时此刻

enter image description here

您将p视为已插入。

编辑:我在解释这个问题时错了。抱歉。我没有看到计算不计算一组。以下是我的答案。我不会抹掉我写的东西,因为我认为它无论如何都很有用。

Ami Tavory是对的

如果您使用扩展树,即在每个节点中存储子树基数,那么您可以轻松计算密钥的顺序positios。我通常会调用此操作position(key)。如果key不在集合中,则会返回key在树中插入时所具有的位置。

root的inorder位置是左树的基数。

现在,为了计算[a,b)设置的基数,你执行position(b) - position(a)。如果树中没有a或b,则可能需要进行一些调整。但基本上就是这样。

我认为

position(key)“自然”简单。假设使用COUNT(node)

访问节点基数
long position(Node * root, const Key & key) noexcept
{
  if (r == Node::NullPtr)
    return 0;

  if (key < KEY(root)) 
    return position(LLINK(r), key, p);
  else if (KEY(r) < key) 
    return position(RLINK(r), key) + COUNT(LLINK(r)) + 1;
  else // the root contains key
    return COUNT(LLINK(r));
}

由于avl树是平衡的,位置需要O(lg n)。所以两个调用需要O(lg n)。非递归版本很简单。

我希望你知道原谅我的错误