【问题标题】:How can I calculate the level of a node in a perfect binary tree from its depth-first order index?如何根据深度优先级索引计算完美二叉树中节点的级别?
【发布时间】:2012-05-30 02:21:21
【问题描述】:

我有一棵完美的二叉树,即树中的每个节点要么是叶节点,要么有两个子节点,所有叶节点都在同一级别。每个节点都有一个深度优先的索引。

(例如,在具有 3 层的树中,根节点的索引为 0,第一个孩子有 1,第一个孩子的第一个孩子有 2,第一个孩子的第二个孩子有 3,第二个孩子有 4,第二个孩子的第一个孩子有 5,第二个孩子的第二个孩子有索引 6。

      0
    /   \
  1      4
 / \    / \
2   3  5   6

)

我知道树的大小(节点数/最大级别),但只知道特定节点的索引,我需要计算它的级别(即它到根节点的距离)。我怎样才能最有效地做到这一点?

【问题讨论】:

  • 这不是二叉树,如果一个节点可以有 >2 个子节点。
  • 请阅读问题:“这是深度优先的,但不是完美的二叉树”
  • 你还需要知道节点总数,否则级别可能无法计算。
  • @nsinreal 好吧,这个问题具有误导性。它声明“我有一棵完美的二叉树”和“这是深度优先的,但不是一棵完美的二叉树”
  • @Justin 好吧,这只是深度优先顺序的一个例子

标签: algorithm binary-tree depth-first-search


【解决方案1】:

编辑:第 1 次尝试...仅适用于 BFS。

如果完美二叉树是指具有堆状结构的二叉树,则可以使用以下公式计算节点的父索引:

parentIndex = (index-1)/2

因此,您可以重复该公式,直到达到

编辑:尝试编号 2..

我能想到的最好方法是 O(index + log n),即 O(n)。执行 DFS 直到达到所需的索引,然后使用父指针继续向上树,直到到达根,跟踪你向上的次数。这假设每个节点上都存在一个父指针。

【讨论】:

  • 不,因为他以深度优先顺序使用索引。
  • @Justin "O(index + log n) 仍然是 O(log n)" -> 没有 O(index + log n) 是 O(n)
  • @Thomash D'oh!又是一次好收获!
【解决方案2】:

看来直接在树上走应该就够效率了。

在算法的每一步,记住你所在节点的子树上的索引范围。范围的第一个值是根节点,之后的前半部分是左侧子树的范围,后半部分应该是右子树的范围。然后您可以递归地向下移动,直到找到您的节点。

例如,让我们在具有 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 开始索引,您也应该能够轻松地适应这一点。

【讨论】:

  • 这似乎也是正确的,并且非常适合易于维护的解决方案,但可能不符合我的性能要求。
  • @hrehfeld:实际上,它应该符合性能要求。无论您从中得到什么算法,都应该与 Tomash 等人提出的非常相似。
【解决方案3】:

如果你只有索引,你就找不到深度。

假设你有一棵这样的树:

    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)

【讨论】:

  • 我们谈论的是完美二叉树。 “我有一棵完美的二叉树,即树中的每个节点要么是叶节点,要么有两个孩子。”
  • 我的示例树不满足该标准吗?所有节点都有 0 或 2 个子节点。
  • 我认为“完美”树通常代表“完全填充”。如,如果您有 k 个级别,那么您有 2^k - 1 个节点。
  • 好点。我为完全填充的树添加了一个额外的示例。
  • 是的,但是我们知道树的大小
【解决方案4】:

未经测试:

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是树中的节点总数。

【讨论】:

  • (count + 1) / 2 - 1 好像有点奇怪,为什么不用(count - 1) / 2
【解决方案5】:

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 只选择好的子树。

【讨论】:

  • 我刚刚添加了一些解释。
  • 嗯,现在我明白了它是如何工作的,但在我看来,“i = i%n 只选择好的子树”这句话。是错的。似乎您正在“替换坐标”以使算法在子树中工作,其中 root = 0。顺便说一句,不错的算法。
【解决方案6】:

所以,我们有这样的树,有 4 层:

          0             - 0th level
      /       \         
     1          8       - 1th level
   /  \       /  \      
  2    5     9    12    - 2th level
 / \   /\   / \   / \
3   4 6  7 10 11 13 14  - 3th level

如您所见,每个左孩子的根索引都增加了 1(左 = 根 + 1),因为在 DFS 中左孩子总是首先访问。 第二个节点的左节点索引增加了左子树的大小(右=左+左大小)。如果我们知道树的深度,我们可以计算它的大小(大小 = 2^depth - 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);
    }

【讨论】:

  • P.S:对不起,我的英语不好
【解决方案7】:

这里有另一个建议可以让这个问题的解决更容易:

如果您以广度优先顺序使用索引标记节点,则无需在 O(1) 时间内进行任何遍历即可计算级别。因此,如果您正在执行多个查询,则可以执行 O(N) BFT 并在 O(1) 时间内回答每个查询。

等级的公式为:

level = floor(log(index + 1))

原木到基数2的地方

在这棵树上试一试:

       0
     /    \
    /      \
   1        2
  / \      / \
 /   \    /   \
3     4  5     6

干杯。

【讨论】:

  • 抱歉,布局被特别要求为深度优先。
猜你喜欢
  • 2020-08-07
  • 2012-10-04
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
  • 2010-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多