如何实现没有父指针的AVL树的插入?

发布于 2024-12-01 22:27:31 字数 271 浏览 1 评论 0原文

我看到一些关于AVL的rebalance()函数实现的文章。
每次插入后,我们应该检查插入节点的祖先是否平衡。
所以我认为,为了检查祖先的余额,我必须了解插入节点的父节点。

但是,我想知道是否有其他方法可以在不使用父指针的情况下做到这一点?
例如,节点结构:

struct Node {
    int data;
    struct Node *lchild, *rchild; //*parent;
};

I saw some articles about the implementation of AVL's rebalance() function.
After each insertion, we should check the insertion Node's ancestors for balance.
So I think, in order to check ancestors' balance, I got to know the insertion Node's parent.

But, I wonder is there any other way to do that without having to use the parent pointer?
e.g., the node struct:

struct Node {
    int data;
    struct Node *lchild, *rchild; //*parent;
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

棒棒糖 2024-12-08 22:27:31

如果我没记错我的数据结构作业:

您所做的是将平衡因子作为 int 存储在节点本身中,它是:

  • -1:节点的左子树比右子树高一级( left-heavy)
  • 0 节点平衡;或
  • 1 右子树较高(右重)。

insert(Node subtree) 函数返回一个布尔值,如果插入使子树的高度增加,则该值为 true。当您从递归 insert() 调用返回时,您可以更新平衡因子并重新平衡树。

这可能可以用几个例子来最好地解释:

如果当前节点处于平衡因子 -1 处,则您将插入到子树中,并且 insert(rchild) 返回true,则:

  1. 将当前节点的平衡因子更新为0 - 左子树在插入前较高,右子树的高度增加,因此它们是现在的高度相同;并
  2. 返回 false - 较浅的树的高度增加,因此当前节点的高度保持不变

如果您要插入任一子树,并且 insert(...) 返回 false

  1. 当前节点的平衡因子不变 - 子树高度与之前相同,平衡性也
  2. 返回 false - 子树高度没有改变,所以也没有改变具有当前节点高度

如果当前节点的平衡因子为0,则您将插入到子树中,并且 insert(lchild) 返回true:

  1. 平衡因子变为-1 - 子树在插入前高度相同,插入使左边的树更高
  2. 返回 true

(类似地,如果插入到右子树,平衡因子将变为1.)


如果当前节点的平衡因子为-1,则插入到子树中,并且 insert(lchild) 返回true

平衡因子将更改为-2,这意味着您必须通过进行适当的旋转来重新平衡节点。我承认我对四次旋转中的每一次对平衡因子的影响以及插入(当前)将返回的内容一无所知,希望前面的示例充分解释了跟踪节点平衡的方法。

If I remember my data structures homework correctly:

What you do is store the balance factor in the node itself as an int that's either:

  • -1: the node's left subtree is a level higher than the right one (left-heavy)
  • 0 the node is balanced; or
  • 1 the right subtree is higher (right-heavy).

You insert(Node subtree) function returns a boolean, which is true if the insertion made the height of subtree increased. You update the balance factor and rebalance the tree as you return from the recursive insert() calls.

This is probably best explained with a few examples:

If the current node is at balance factor -1, you're inserting into the right subtree, and insert(rchild) returns true, you:

  1. update the balance factor of the current node to 0 - the left subtree was higher before the insertion, and the right subtree's height increased, so they're the same height now; and
  2. return false - the shallower tree's height increased, so the current node's height stays the same

If you're inserting into either subtree, and insert(…) returns false:

  1. the balance factor of the current node is unchanged - the subtree heights are the same as before, and so is the balance
  2. return false - the subtree heights haven't changed, so neither has the current node height

If the current node's balance factor is 0, you're inserting into the left subtree, and insert(lchild) returns true:

  1. the balance factor changes to -1 - the subtrees were the same height before the insertion, and the insertion made the left one higher
  2. return true

(Analogously, if inserting into the right subtree, the balance factor will change to 1.)


If the current node's balance factor is -1, you're inserting into the left subtree, and insert(lchild) returns true:

The balance factor would change to -2, which means you have to rebalance the node by doing the appropriate rotation. I'll admit I'm drawing a blank at what each of the four rotations will do to the balance factor and what insert(current) will return, hopefully the previous examples explain the approach to tracking the nodes' balance sufficiently.

原野 2024-12-08 22:27:31

当你遍历树的时候,你可以维护一个到当前节点的栈

stack<Node*> nodeStack;

,当你遍历到一个新的节点时,将它添加到栈中,然后你就有了你的祖先。
当处理完一个节点后,将其从堆栈中弹出。

** 编辑 **

对齐注释的详细说明:

struct Node {
    int data;
    struct Node *children, *parent
};

创建子项时,这样做:

node.children = new Node[2]; or node.children = malloc(sizeof(Node) * 2);
node.children[0].parent = node;
node.children[1].parent = node;

You can maintain a stack to the current node while you are traversing the tree

stack<Node*> nodeStack;

When you traverse to a new node, add it to the stack, and then you have your ancestry.
When you finish processing a node, pop it from the stack.

** Edit **

Elaboration for the alignment comment:

struct Node {
    int data;
    struct Node *children, *parent
};

when creating children, do it this way:

node.children = new Node[2]; or node.children = malloc(sizeof(Node) * 2);
node.children[0].parent = node;
node.children[1].parent = node;
花辞树 2024-12-08 22:27:31

使用双指针(或对 C++ 要求的指针的引用)应该完全消除对父指针的需要。

typedef struct Node {
    int value;
    int height;
    struct Node *left;
    struct Node *right;
} Node;

int height(Node *node) {
    return (node == NULL) ? -1 : node->height;
}

void insert(Node * & node, int value) {
    if (node == NULL) {
        node = new Node();
        node->value = value;
    } else if (value < node->value) {
        insert(node->left, value);
        if (height(node->left) - height(node->right) == 2) {
            if (value < note->left->value) {
                singleRotateWithLeftChild(node);
            } else {
                doubleRotateWithLeftChild(node);
            }
        }
    } else if (value > node->value) {
        // Symmetric case
    }

    node->height = 1 + max(height(node->left), height(node->right));
}

Using double pointers (or references to pointers as you asked for C++) should completely eliminate the need for parent pointers.

typedef struct Node {
    int value;
    int height;
    struct Node *left;
    struct Node *right;
} Node;

int height(Node *node) {
    return (node == NULL) ? -1 : node->height;
}

void insert(Node * & node, int value) {
    if (node == NULL) {
        node = new Node();
        node->value = value;
    } else if (value < node->value) {
        insert(node->left, value);
        if (height(node->left) - height(node->right) == 2) {
            if (value < note->left->value) {
                singleRotateWithLeftChild(node);
            } else {
                doubleRotateWithLeftChild(node);
            }
        }
    } else if (value > node->value) {
        // Symmetric case
    }

    node->height = 1 + max(height(node->left), height(node->right));
}
瞎闹 2024-12-08 22:27:31

由于这个问题没有完整的实现,我决定添加一个。这可以通过使用返回当前节点的递归插入来完成。所以,这是代码:

typedef struct node
{
    int val;
    struct node* left;
    struct node* right;
    int ht;
} node;

int height(node* current) {
    return current == nullptr ? -1 : current->ht;
}

int balanceFactor(node* root) {
    int leftHeight = height(root->left);
    int rightHeight = height(root->right);

    return leftHeight - rightHeight;
}

int calcHeight(node* n) {
    int leftHeight = height(n->left);
    int rightHeight = height(n->right);

    return max(leftHeight, rightHeight) + 1;
}

node* insert(node * root,int val) {
    /**
    First, recusively insert the item into the tree
    */
    if (root == nullptr) {
        root = new node();
        root->val = val;
    } else if (root->val < val) {
        root->right = insert(root->right, val);
        //the height can increase only because of the right node
        root->ht = std::max(root->ht, root->right->ht + 1);
    } else {
        root->left = insert(root->left, val);
        //the height can increase only because of the left node
        root->ht = std::max(root->ht, root->left->ht + 1);
    }

    //after insertion on this depth is complete check if rebalancing is required

    // the right subtree must be rebalanced
    if (balanceFactor(root) == -2) {
        node* r = root->right;
        node* rl = r->left;
        // it's a right right case
        if (balanceFactor(r) == -1) {
            r->left = root;
            root->right = rl;
            root->ht = calcHeight(root);
            r->ht = calcHeight(r);
            //return new root
            return r;
        } else { // it's a right left case
            node* rlr = rl->right;
            node* rll = rl->left;
            rl->left = root;
            root->right = rll;
            rl->right = r;
            r->left = rlr;

            root->ht = calcHeight(root);
            r->ht = calcHeight(r);
            rl->ht = calcHeight(rl);
            return rl;
        }
    } else if (balanceFactor(root) == 2) {
        node* l = root->left;
        node* lr = l->right;
        // it's a left left case
        if (balanceFactor(l) == 1) {
            l->right = root;
            root->left = lr;
            root->ht = calcHeight(root);
            l->ht = calcHeight(l);

            //return new root
            return l;
        } else { // it's a left right case
            node* lrl = lr->left;
            node* lrr = lr->right;
            lr->right = root;
            lr->left = l;
            root->left = lrr;
            l->right = lrl;

            root->ht = calcHeight(root);
            l->ht = calcHeight(l);
            lr->ht = calcHeight(lr);

            return lr;
        }
    }

    return root;
}

As there's no complete implementation for this question I've decided to add one. It could be done by using a recursive insert returning a current node. So, here's the code:

typedef struct node
{
    int val;
    struct node* left;
    struct node* right;
    int ht;
} node;

int height(node* current) {
    return current == nullptr ? -1 : current->ht;
}

int balanceFactor(node* root) {
    int leftHeight = height(root->left);
    int rightHeight = height(root->right);

    return leftHeight - rightHeight;
}

int calcHeight(node* n) {
    int leftHeight = height(n->left);
    int rightHeight = height(n->right);

    return max(leftHeight, rightHeight) + 1;
}

node* insert(node * root,int val) {
    /**
    First, recusively insert the item into the tree
    */
    if (root == nullptr) {
        root = new node();
        root->val = val;
    } else if (root->val < val) {
        root->right = insert(root->right, val);
        //the height can increase only because of the right node
        root->ht = std::max(root->ht, root->right->ht + 1);
    } else {
        root->left = insert(root->left, val);
        //the height can increase only because of the left node
        root->ht = std::max(root->ht, root->left->ht + 1);
    }

    //after insertion on this depth is complete check if rebalancing is required

    // the right subtree must be rebalanced
    if (balanceFactor(root) == -2) {
        node* r = root->right;
        node* rl = r->left;
        // it's a right right case
        if (balanceFactor(r) == -1) {
            r->left = root;
            root->right = rl;
            root->ht = calcHeight(root);
            r->ht = calcHeight(r);
            //return new root
            return r;
        } else { // it's a right left case
            node* rlr = rl->right;
            node* rll = rl->left;
            rl->left = root;
            root->right = rll;
            rl->right = r;
            r->left = rlr;

            root->ht = calcHeight(root);
            r->ht = calcHeight(r);
            rl->ht = calcHeight(rl);
            return rl;
        }
    } else if (balanceFactor(root) == 2) {
        node* l = root->left;
        node* lr = l->right;
        // it's a left left case
        if (balanceFactor(l) == 1) {
            l->right = root;
            root->left = lr;
            root->ht = calcHeight(root);
            l->ht = calcHeight(l);

            //return new root
            return l;
        } else { // it's a left right case
            node* lrl = lr->left;
            node* lrr = lr->right;
            lr->right = root;
            lr->left = l;
            root->left = lrr;
            l->right = lrl;

            root->ht = calcHeight(root);
            l->ht = calcHeight(l);
            lr->ht = calcHeight(lr);

            return lr;
        }
    }

    return root;
}
甜中书 2024-12-08 22:27:31

我编码的方式是,当您在树中搜索要删除的元素时,临时将您遍历(左或右)的子链接更改为遍历节点堆栈中的链接(实际上是临时父指针) 。然后从该堆栈中弹出每个节点,恢复子指针,并重新平衡。

对于 C++ 编码,请参阅 https://github.com/wkaras/C-plus-plus-intrusive-container-templates/blob/master/avl_tree.h

对于 C 编码,请参阅 http: 中的宏调用 L__(remove) 生成名称的函数: //wkaras.webs.com/gen_c/cavl_impl_h.txt

我认为父指针对于插入没有任何用处。

如果您想删除由节点指针而不是唯一键标识的节点,那么我认为使用父指针可能会更快。

The way I coded it is, as you're searching the tree for the element to delete, temporarily change the child link that you traverse (left or right) to be a link in a stack of traversed nodes (effectively a temporary parent pointer). Then pop each node from this stack, restore the child pointer, and rebalance.

For a C++ encoding, see the remove member function (currently at line 882) in https://github.com/wkaras/C-plus-plus-intrusive-container-templates/blob/master/avl_tree.h .

For a C encoding, see the function whose name is generated by the macro invocation L__(remove) in http://wkaras.webs.com/gen_c/cavl_impl_h.txt .

I don't think having a parent pointer is of any use for inserting.

If you want to delete a node identified by a node pointer rather than a unique key, then it will perhaps be faster I think with parent pointers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文