我有一个完美的二叉树,即树中的每个节点都是叶节点,或者有两个子节点,所有叶节点都在同一级别上。每个节点都有一个深度优先的索引。
(例如,在具有3个级别的树中,根节点具有索引0,第一个孩子具有1,第一个孩子的第一个孩子具有2个,第一个孩子的第二个孩子具有3个,第二个孩子具有4个,第二个孩子的第一个孩子有5个,第二个孩子的第二个孩子有6个。
0
/ \
1 4
/ \ / \
2 3 5 6
)
我知道树的大小(节点数/最大级别),但只知道特定节点的索引,我需要计算它的级别(即它到根节点的距离)。我如何最有效地完成这项工作?
答案 0 :(得分:7)
这是另一个建议,可以使这个问题的解决方案更容易:
如果使用广度优先顺序的索引标记节点,则可以在O(1)时间内计算没有任何遍历的级别。因此,如果您正在进行多个查询,则可以执行O(N)BFT并在O(1)时间内回答每个查询。
关卡的公式为:
level = floor(log(index + 1))
日志是基地2
在这棵树上试试看:
0
/ \
/ \
1 2
/ \ / \
/ \ / \
3 4 5 6
干杯。
答案 1 :(得分:4)
让i
成为您要查找的索引,n
为节点总数。
此算法可以满足您的需求:
level = 0
while i != 0 do
i--
n = (n-1)/2
i = i%n
level++
done
0是根的索引,如果i = 0
那么你处于良好的水平,否则你可以删除根并获得两个子树n = (n-1)/2
更新节点数是新树(这是旧子树的子树),i = i%n
仅选择好的子树。
答案 2 :(得分:3)
似乎直接在树上行走应该足够有效。
在算法的每个步骤中,请记住您所在节点的子树上的索引范围。范围的第一个值是根节点,之后是前半部分是左边子树的范围,后半部分应该是右子树的范围。然后,您可以递归向下移动,直到找到您的节点。
例如,让我们在包含15个元素的4级树中搜索
(root node)(left elements)(right elements)
Starting range: (0)(1 2 3 4 5 6 7)(8 9 10 11 12 13 14)
Go left : (1)(2 3 4)(5 6 7)
Go right : (5)(6)(7)
Found node, total depth 2
你应该能够通过一个简单的循环来做到这一点,只使用几个变量来存储范围的开始和结束。如果您进行一些小的更改,例如使用post / pre / in-order遍历或启动索引形式1而不是0,您也应该能够轻松地进行调整。
答案 3 :(得分:3)
未测试:
int LevelFromIndex( int index, int count)
{
if (index == 0)
return 0;
if (index > (count - 1)/ 2)
index -= (count - 1) / 2;
return 1 + LevelFromIndex( index - 1, (count - 1) / 2);
}
此处count
是树中节点的总数。
答案 4 :(得分:0)
编辑:尝试编号1 ...仅适用于BFS。
如果通过完美的二叉树,您的意思是具有类似堆结构的二叉树,那么您可以使用以下公式计算节点的父索引:
parentIndex = (index-1)/2
所以你可以重复那个公式,直到你达到< = 0,每次循环你基本上都会在树上达到一个级别。
编辑:尝试编号2 ..
我能想到的最佳方法是采用O(index + log n),即O(n)。在达到所需索引之前执行DFS,然后使用父指针继续向上移动树,直到达到根跟踪您已经上升的次数。这假设每个节点上都存在父指针。
答案 5 :(得分:0)
如果您拥有的只是索引,则无法找到深度。
假设你有一棵这样的树:
1
/ \
2 5
/ \
3 4
索引为3的节点深度为2。
假设你有一棵这样的树:
1
/ \
2 3
/ \
4 5
索引为3的节点的深度为1。
仅仅通过了解它们的索引就无法区分这两棵树。只知道索引就无法找到与根的距离。
编辑:如果你的意思是一个完美的二叉树,所有的叶子都在同一个深度,每个父母都有两个孩子,那么你仍然找不到深度。
比较这两棵树:
1
/ \
2 3
1
/ \
2 5
/ \ / \
3 4 6 7
节点3的深度根据树的高度而变化。
编辑2:如果你知道总树的高度,你可以使用这个递归算法:
def distanceFromRoot(index, rootIndex, treeHeight):
if index == rootIndex:
return 0
leftIndex = rootIndex+1
rightIndex = rootIndex + 2**treeHeight
if index >= rightIndex:
return 1 + distanceFromRoot(index, rightIndex, treeHeight-1)
else:
return 1 + distanceFromRoot(index, leftIndex, treeHeight-1)
答案 6 :(得分:0)
所以,我们有4个级别的树:
0 - 0th level
/ \
1 8 - 1th level
/ \ / \
2 5 9 12 - 2th level
/ \ /\ / \ / \
3 4 6 7 10 11 13 14 - 3th level
正如你所看到的,每个左边的孩子都有一个根的索引增加了一个(左= root + 1),因为在DFS中,左边的孩子总是先访问。 第二个节点的左节点索引增加了左子树的大小(right = left + leftSize)。如果我们知道树的深度,我们可以计算它的大小(大小= 2 ^深度-1)。至于左子树的深度等于父亲的深度减一,其大小= 2 ^(parentDepth - 1) - 1。
现在我们有一个算法 - 计算左节点的索引,计算右节点的索引。如果节点索引位于它之间,则转到左节点,否则 - 转到右节点。
代码:
static int level(int index, int root, int treeDepth) {
if (index == root)
return 0;
if (treeDepth <= 0 /* no tree */ || treeDepth == 1 /* tree contains only root */)
throw new Exception("Unable to find node");
int left = root + 1;
int right = left + (int)Math.Pow(2, treeDepth - 1) - 1;
if (index == left || index == right)
return 1;
if (left < index && index < right)
return 1 + level(index, left, treeDepth - 1);
else
return 1 + level(index, right, treeDepth - 1);
}