在O(n log n)中给定二进制树的每个子树中的节点大于根

时间:2016-01-23 22:52:39

标签: algorithm data-structures tree

我们获得了一个带有n个节点的树,其形式为指向其根节点的指针,其中每个节点包含指向其父节点,左子节点和右子节点的指针,以及一个整数键。对于每个节点v,我想添加其他字段v.bigger,其中应包含密钥大于v.key的节点数,这些节点位于以v为根的子树中。将这样的字段添加到树的所有节点总共需要O(n log n)次。

我正在寻找可以解决这个问题的任何提示。我尝试了几种启发式方法 - 例如,在考虑以自下而上的方式解决此问题时,对于固定节点vv.leftv.right可以提供某种类型v set(平衡BST?)与操作bigger(x)的关系,对于给定的x,它在对数时间内返回大于x的元素。问题是,我们需要在O(log n)中合并这些集合,所以这似乎是不行的,因为我不知道任何有序的集合,如支持快速合并的数据结构。

我还考虑过自上而下的方法 - 当且仅当v位于简单路径上时,节点u.bigger才会为某个节点u添加一个u到根和u<v。因此,v可以某种方式更新所有此类u,但我无法想出任何合理的方法......

那么,思考这个问题的正确方法是什么?

3 个答案:

答案 0 :(得分:5)

在给定树中执行深度优先搜索(从根节点开始)。

当第一次访问任何节点(来自父节点)时,将其密钥添加到某个订单统计数据结构(OSDS)。同时查询OSDS以获取大于当前密钥的密钥数量,并使用此查询的否定结果初始化v.bigger

当最后一次访问任何节点(来自右子)时,查询OSDS以获取大于当前密钥的密钥数量,并将结果添加到v.bigger

您可以将此算法应用于任何有根树(不一定是二叉树)。并且它不一定需要父指针(您可以使用DFS堆栈)。

对于OSDS,您可以使用增强的BST或Fenwick树。在Fenwick树的情况下,您需要预处理给定的树,以便压缩键的值:只需将所有键复制到数组,排序,删除重复项,然后通过其数组中的索引替换键。

答案 1 :(得分:1)

基本理念:

使用自下而上的方法,每个节点将从两个子中获得子树中值的两个有序列表,然后找出它们中有多少更大。完成后,向上传递组合的有序列表。

详细信息:

  1. 叶:
    叶子显然有v.bigger=0。它们上面的节点创建两个值的项列表,更新自身并将其自己的值添加到列表中。
  2. 所有其他节点:
    从儿子那里获取两个列表并以有序的方式合并它们。由于它们已经排序,因此为O(number of nodes in subtree)。在合并期间,您还可以查找有多少节点限定条件并获取节点的v.bigger值。
  3. 为什么这是O(n登录)?

    树中的每个节点都会计算其子树中的节点数。这意味着根计算树中的所有节点,根的子节点每个计数(组合)树中节点的数量(是,是,-1为根),依此类推。相同的高度一起计算较低的节点数。这使我们计算的节点数为number of nodes * height of the tree - 即O(n logn)

答案 2 :(得分:-1)

如果对于每个节点,我们保留一个单独的二叉搜索树(BST),该树由包含在该节点的子树的节点组成。

对于级别为.va-pickers .va-picker.selected{ background:black; color:white; } 的节点v,合并两个具有k元素的子树v.leftv.rightO(n/2^(k+1))。在为此节点形成BST之后,我们可以通过仅计算BST的右(传统)子树中的元素来在O(n/2^k)时间内找到v.bigger。总而言之,我们对O(n/2^(k+1))级别的单个节点进行了O(3*n/2^(k+1))次操作。总共有k个k级节点,因此我们将2^k简化为O(n)(降低3/2常数)。级别O(2^k*3*n/2^(k+1))的操作。有k级别,因此我们总共有log(n)次操作。