我们获得了一个带有n
个节点的树,其形式为指向其根节点的指针,其中每个节点包含指向其父节点,左子节点和右子节点的指针,以及一个整数键。对于每个节点v
,我想添加其他字段v.bigger
,其中应包含密钥大于v.key
的节点数,这些节点位于以v
为根的子树中。将这样的字段添加到树的所有节点总共需要O(n log n)
次。
我正在寻找可以解决这个问题的任何提示。我尝试了几种启发式方法 - 例如,在考虑以自下而上的方式解决此问题时,对于固定节点v
,v.left
和v.right
可以提供某种类型v
set(平衡BST?)与操作bigger(x)
的关系,对于给定的x
,它在对数时间内返回大于x
的元素。问题是,我们需要在O(log n)
中合并这些集合,所以这似乎是不行的,因为我不知道任何有序的集合,如支持快速合并的数据结构。
我还考虑过自上而下的方法 - 当且仅当v
位于简单路径上时,节点u.bigger
才会为某个节点u
添加一个u
到根和u<v
。因此,v
可以某种方式更新所有此类u
,但我无法想出任何合理的方法......
那么,思考这个问题的正确方法是什么?
答案 0 :(得分:5)
在给定树中执行深度优先搜索(从根节点开始)。
当第一次访问任何节点(来自父节点)时,将其密钥添加到某个订单统计数据结构(OSDS)。同时查询OSDS以获取大于当前密钥的密钥数量,并使用此查询的否定结果初始化v.bigger
。
当最后一次访问任何节点(来自右子)时,查询OSDS以获取大于当前密钥的密钥数量,并将结果添加到v.bigger
。
您可以将此算法应用于任何有根树(不一定是二叉树)。并且它不一定需要父指针(您可以使用DFS堆栈)。
对于OSDS,您可以使用增强的BST或Fenwick树。在Fenwick树的情况下,您需要预处理给定的树,以便压缩键的值:只需将所有键复制到数组,排序,删除重复项,然后通过其数组中的索引替换键。
答案 1 :(得分:1)
基本理念:
使用自下而上的方法,每个节点将从两个子中获得子树中值的两个有序列表,然后找出它们中有多少更大。完成后,向上传递组合的有序列表。
详细信息:
v.bigger=0
。它们上面的节点创建两个值的项列表,更新自身并将其自己的值添加到列表中。 O(number of nodes in subtree)
。在合并期间,您还可以查找有多少节点限定条件并获取节点的v.bigger
值。为什么这是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.left
和v.right
为O(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)
次操作。