简体   繁体   English

C ++-删除AVL树中的节点

[英]C++ - removal of a node in an AVL Tree

I'm currently trying to implement an AVL tree in C++, so far I've done pretty much everything except a node removal. 我目前正在尝试在C ++中实现AVL树,到目前为止,除删除节点外,我已经做了几乎所有事情。

1) Can someone confirm that my algorithm for removing a node is correct? 1)有人可以确认我的删除节点算法正确吗?

  • Find the node to delete in the tree 在树中找到要删除的节点
  • if the node has 0 child : delete the node 如果节点有0个孩子:删除节点
  • else, if the node has 1 child: delete the node, and re-link his child 否则,如果节点有1个子节点:删除该节点,然后重新链接其子节点
  • else (2 children): find its successor, and swap the node to delete with its successor. else(2个孩子):找到其后继者,并用其后继者交换要删除的节点。 then repeat the steps to delete the node (which is now where its successor was). 然后重复步骤以删除节点(现在是其后继节点所在的节点)。
  • Rebalance the tree after the insertion (it'll rebalance the tree recursively...) 插入后重新平衡树(它将以递归方式重新平衡树...)

I've done this, but the part where I'm not sure, is the step where I delete the node. 我已经做到了,但是我不确定的部分是删除节点的步骤。 Do I have to delete the reference to the node also, or it'll be managed? 我是否还必须删除对该节点的引用,否则将对其进行管理? (Because I've passed in parameter a Node*&, but the successor is only a Node* in the function...) (因为我已经传递了Node *&参数,但是后继函数只是函数中的Node * ...)

I'm not sure I'm being super clear, let me know if you need more details. 我不确定我是否非常清楚,如果您需要更多详细信息,请告诉我。

(I would post some code, but unfortunately I speak french, so you won't understand much from it, I guess) (我会发布一些代码,但是不幸的是我会说法语,所以我想您不会对此很了解)

If the node was created using 'new' than it needs to be explicitly deleted, absolutely. 如果绝对使用“ new”创建节点,则需要显式删除该节点。 They question will be where . 他们的疑问将在哪里 If you are deleting the node, (the assumption is nobody else needs the contents of the node) then you should also delete the node itself after completing the nodes removal from the tree. 如果要删除节点(假设没有其他人需要该节点的内容),那么在完成从树中删除节点后,还应该删除节点本身。

Now, the reference to the node is another matter. 现在,对节点的引用是另一回事。 If the reference is an artifact of a method argument definition, you don't have to do anything with it. 如果引用是方法参数定义的工件,则无需对其进行任何处理。 The reference was created on the stack to help with the method call. 该引用是在堆栈上创建的,以帮助进行方法调用。 If I remember my C++, references can never be null, which means never having to say you're sorry(bad joke), er never having to delete them. 如果我还记得我的C ++,则引用永远不能为null,这意味着永远不必说对不起(恶作剧),也不必删除它们。

[EDIT] The OP says, "Because I've passed in parameter a Node*&, but the successor is only a Node* in the function...)" so I assumed you meant something like: [编辑] OP说,“因为我已经向参数传递了Node *&,但是后继函数只是函数中的Node * ...”,所以我假设您的意思是:

void removeNode(Node*& node) 

which is then called using 然后使用

void foo()
{
    Node* n = new Node();
    // for example
    removeNode(n);
}

My C++ is a bit rusty but the idea here is that 'n' is a pointer but the argument type of removeNode() is a reference to a pointer. 我的C ++有点生锈,但是这里的想法是'n'是一个指针,但是removeNode()的参数类型是对指针的引用。 The caller doesn't know that a reference is involved, it just passes 'n' expecting the arg type to be pointer-to-node (Node*). 调用者不知道涉及到引用,它只是传递“ n”,期望arg类型为指针到节点(Node *)。 The compiler is creating the reference as a wrapper around the argument so only the callee is aware of the reference. 编译器将引用创建为参数周围的包装器,因此只有被调用者才能知道引用。 Since the reference is created on the stack, it will be 'managed' properly when removeNode() returns. 由于引用是在堆栈上创建的,因此在removeNode()返回时将对其进行“托管”。 The node pointed to by 'n' still needs to be deleted, the question is which code should handle it. 'n'指向的节点仍然需要删除,问题是哪个代码应该处理它。

First thought would be for 'removeNode()' to do it. 首先想到的是让'removeNode()'做到这一点。 One problem is that it only has a reference to it and if you delete the pointer (the target of the reference) the reference will be null which is a bad idea / not allowed. 一个问题是它只有一个引用,如果删除指针(引用的目标),引用将为null,这是一个坏主意/不允许。 Just thinking of the syntax to attempt it made me cringe. 只是想尝试语法就让我畏缩了。

So, have the client code to do it, like so: 因此,请让客户端代码执行此操作,如下所示:

void foo()
{
    Node* n = new Node();
    // for example
    removeNode(n);
    delete n;
}

Basically , you need to have a plan for the scope of your node pointers. 基本上 ,您需要为节点指针的范围制定一个计划。 As long as it is consistent, you could do it in several different ways. 只要它是一致的,就可以用几种不同的方式来做到这一点。 If you wanted removeNode() to handle the deletion, then change the argument type to a pointer instead of a reference and document the call so the expectation is it both removes the node from the tree and deletes the memory as well. 如果您希望removeNode()处理删除,则将参数类型更改为指针而不是引用,并记录该调用,因此期望它既从树中删除该节点,又删除该内存。

The tricky part with your question is that "deleting the reference to the node" is actually rather vague: depending on what you consider "deleting the reference", the answer can be 'yes' or 'no'. 问题的棘手部分是“删除对节点的引用”实际上相当模糊:根据您认为“删除引用”的不同,答案可以是“是”或“否”。

As I see it, you are going to "delete the reference to the node" as part of relinking the tree. 如我所见,您将“删除对节点的引用”作为重新链接树的一部分。 The crucial part is the fact that your method receives the node pointer as Node *& : with this, your method doesn't just get the pointer to the node under scrutiny, it receives a reference to where this pointer is stored in the parent node . 关键部分是您的方法将节点指针作为Node *&接收:事实上,您的方法不仅获得了经过仔细检查的节点的指针,而且还收到了对该指针在父节点中存储位置的引用。 。 You have to modify this reference in order to keep the tree linkage intact. 您必须修改此引用,以保持树链接完整。

So if your method does something like this: 因此,如果您的方法执行以下操作:

void deleteNode (Node*& node) {
    if (node is the right one to delete) {
        Node * tmp = node;
        node = node->successor;
        delete node;
    }
    else
        deleteNode(node->successor);
}

the assignment node = node->successor actually modifies the previous nodes' successor field. 分配node = node->successor实际上修改了先前节点的后继字段。 Without this assignment, your tree structure would be damaged, as the previous nodes' successor field would point to a now-deallocated Node. 没有此分配,您的树结构将被破坏,因为先前节点的后继字段将指向现在已重新分配的节点。

I you are coding in standard C++ then nothing will be "managed" for you. 如果您使用标准C ++进行编码,则不会为您“管理”任何内容。 If you use smart pointers then they will take care of the deallocations for you though. 如果您使用智能指针,那么它们将为您处理释放。

As for your interface, I suggest the remove() function takes a pointer to the node to delete. 至于您的接口,我建议remove()函数采用指向要删除的节点的指针。 Then you don't need to bother with finding the node, as it is given. 这样,您就不必为找到该节点而烦恼了。 Do write a find() function though, so clients can find the node themselves. 不过,请务必编写find()函数,以便客户端可以自己找到该节点。

Finally, I am not sure I follow your third case, where a node to delete has two children. 最后,我不确定我是否遵循您的第三种情况,要删除的节点有两个孩子。 In an AVL tree when a node to delete as two children you can swap it with the right-most child of the left subtree of the node to delete. 在AVL树中,当要删除的节点作为两个子节点时,可以将其与要删除的节点的左子树的最右边子节点交换。 Once swapped, the node to remove has exactly one or zero children, so you essentially reduced the problem to these two cases. 交换后,要删除的节点恰好具有一个或零个子节点,因此从本质上将问题简化为这两种情况。

For example: 例如:

            20
     40            15
  60    35     18       10
80    38  30 19  17   13   8

In the above tree if you wish to remove node 20 then you swap it with node 30 (the child of node 35 ): 在上面的树中,如果要删除节点20则将其与节点30 (节点35的子节点)交换:

            30
     40            15
  60    35     18       10
80    38  20 19  17   13   8

And then remove node 20 as a child-less node. 然后将节点20删除为无子节点。 If there were no node 30 then the right-most child of the left subtree would be node 35 , and you'd swap node 20 with that. 如果没有节点30则左子树的最右边的子节点将是节点35 ,然后将其与节点20交换。 Then you'd have to remove a node with a single child, which you know how to do. 然后,您必须删除具有单个子节点的节点,您知道该怎么做。

Do note that until you're done with the deletion the tree is in an inconsistent state. 请注意,在删除操作完成之前,树处于不一致状态。 This is important if you're working concurrently. 如果您同时工作,这一点很重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM