【问题标题】:Iterating over AVL tree in O(1) space without recursion在 O(1) 空间中迭代 AVL 树而不递归
【发布时间】:2023-03-14 01:25:01
【问题描述】:

我有一个 AVL 树。每个节点如下所示:

typedef struct {
    Node *parent;  // the parent node
    Node *left;    // the node left of this node
    Node *right;   // the node right of this node
    int height;    // the height of this node
    void *value;   // payload
} Node;

是否可以在 O(1) 空间中迭代具有这些节点的 AVL 树,无需递归,如果可以,如何?

如果没有,也可以使用具有 sub-O(n) 空间或当且仅当需要 O(N) 空间的解决方案。

通过遍历树我的意思是我想访问每个节点一次,如果可能的话,按顺序(从最左边到最右边的节点)。

【问题讨论】:

  • 家庭作业,对吧?你试过什么?
  • @Jim Balter:我希望不是家庭作业。我今年 17 岁,还在上高中(或者至少是荷兰语版本)。它用于 Python 项目,并用于迭代器中。
  • 您希望以何种方式“迭代 AVL 树”?
  • @Gumbo:糟糕,忘记在问题中说了。我想访问每个节点一次,如果可能的话,按顺序(从最左边到最右边的节点)。
  • 再一次,你有什么尝试?当然,您至少可以找到通往第一个(最左边)节点的路……弄清楚下一步该做什么。

标签: c algorithm data-structures


【解决方案1】:

如果你存储你访问过的最后一个节点,你可以在迭代器中推导出下一个要访问的节点。

  • 如果最后一个节点是您的父节点,则沿左子树向下。
  • 如果最后一个节点是您的左子树,请沿着右子树向下。
  • 如果最后一个节点是您的右子树,请转到您的父节点。

这个算法让你​​在 O(1) 中遍历树。你需要为叶子充实一点,并决定你想要什么样的迭代器(前/中/后序)来决定迭代器应该在哪里并等待增量。

【讨论】:

  • 当然!这使我可以通过从最左边的父节点和设置为最左边节点的最后一个节点开始按顺序遍历树。谢谢!
  • @nightcracker 您可以从根节点开始迭代树,最后一个节点设置为 NULL(根节点的父节点)。
【解决方案2】:

只要您保留父指针,就可以通过给定指向某个节点的指针来获取下一个有序节点。这可用于迭代树,从最左边的节点开始。来自my implementation of AVL tree

BAVLNode * BAVL_GetNext (const BAVL *o, BAVLNode *n)
{
    if (n->link[1]) {
        n = n->link[1];
        while (n->link[0]) {
            n = n->link[0];
        }
    } else {
        while (n->parent && n == n->parent->link[1]) {
            n = n->parent;
        }
        n = n->parent;
    }

    return n;
}

获取最左边的节点:

BAVLNode * BAVL_GetFirst (const BAVL *o)
{
    if (!o->root) {
        return NULL;
    }

    BAVLNode *n = o->root;
    while (n->link[0]) {
        n = n->link[0];
    }

    return n;
}

这里,node->link[0]node->link[1] 分别是节点的左右子节点,node->parent 是指向父节点的指针(或 NULL 表示根节点)。

单个 GetNext() 操作具有 O(logn) 时间复杂度。但是,当用于迭代整个树时,您会得到 O(n) 的摊销时间复杂度。

【讨论】:

  • 这不起作用。例如,对于根没有右链接的任何树,它都会返回 NULL ......好吧,也许它适用于您的编辑,从最左边的节点开始。
  • 确实工作。如果您调用 GetNext(root) 并且根没有正确的链接,那么是的,它返回 NULL - 因为根是 最后一个节点,并且没有下一个节点。如果你调用 GetNext(root 的左孩子的最右边的孩子),它会返回 root(不是 NULL)。此外,我已经在 20 个不同的地方使用了该代码,没有任何问题。
  • @Jim:显然你必须从最左边的节点开始,而不是根节点,以便按顺序迭代树。
  • 这并不明显。例如,请参阅 thiton 的回答和我对此的评论。
  • @Jim:我在回答的开头提到 GetNext() 返回给定节点的下一个 in-order 节点。因此,您必须从具有最小值的节点开始,即最左边的节点,以便通过重复调用 GetNext() 来获取所有节点。很明显,与从头到尾迭代 链表 的方式相同,您必须从第一个节点开始,而不是某个中间节点。
【解决方案3】:

Harry Lewis 和 Larry Denenberg 的“数据结构及其算法”描述了二叉树的常数空间遍历的链接反转遍历。为此,您不需要每个节点的父指针。遍历使用树中现有的指针来存储用于回溯的路径。需要 2-3 个额外的节点引用。在每个节点上加上一点,以便在我们向下移动时跟踪遍历方向(向上或向下)。在我对书中这些算法的实现中,分析表明这种遍历的内存/处理器时间要少得多。 java中的一个实现(我猜c会更快)是here

【讨论】:

    猜你喜欢
    • 2014-05-27
    • 1970-01-01
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-26
    • 2019-12-01
    相关资源
    最近更新 更多