【问题标题】:Deleting Root Node of a Binary Search Tree删除二叉搜索树的根节点
【发布时间】:2014-07-01 08:25:43
【问题描述】:

我有这个功能用于删除二叉搜索树中的一个节点,这似乎在我要求它删除根节点的情况下工作。它应该取左边最右边的值并用那个替换节点;但是,一旦发生这种情况,新根节点的子节点指针似乎不会指向原始根节点的子节点。代码如下:

bool delete_node(Node*& root, TYPE data) {
Node* toDelete;
Node* parent;

// This function is defined appropriately elsewhere, and finds the target to be deleted
toDelete = find(data, root);

if (!toDelete) {
    return false;
}

// This function is defined appropriately elsewhere, and finds the parent of the node to be deleted
parent = find_parent(root, toDelete);

// Other cases left out because they work
// If the target node has two children:
if (toDelete->left && toDelete->right) 
{

    // find rightmost child on left that is a leaf
    Node *replacement = toDelete->left;
    while (replacement->right) 
    {
        replacement = replacement->right;
    }

    // set the target node's data
    toDelete->data = replacement->data;
    if (parent) 
    {
        if ( parent->data < toDelete->data ) 
        {
            parent->right = replacement;
        } else
        {
            parent->left = replacement;
        }
    } else 
    {
        // if node has no parents, then it is the root and should be replaced with replacement
        // This line here is what seems to be causing my trouble...I think
        root = replacement;
    }
    parent = find_parent(toDelete, replacement);
    if (parent) 
    {
        if (parent->left == replacement)
            parent->left = NULL;
        else
            parent->right = NULL;
    }
    delete toDelete;
    return true; 
}
}

提前致谢!

【问题讨论】:

  • 听起来您需要多考虑一下您的算法。在您牢牢掌握算法及其不变量之前,不要开始编写代码。
  • 如果代码适用于除删除根节点之外的所有情况,那么您可以只需将根设置为 -infinity 并且永远不要删除根节点...
  • 您可能希望为代码导致的每次更改创建树图。如果您想真正彻底,请为每个可能的场景创建树的状态图,显示在该场景中应用每个功能后出现的树。发现这通常有助于解决此类问题。并且不要将其绘制成它应该看起来的样子,而是根据你当前的代码绘制它的样子。这样你可能会发现错误。
  • @cluemein 为每一步绘制图表确实很有帮助,我设法弄清楚了。谢谢!
  • @eggrollers 您可以发布自己问题的答案并稍后接受。这样做是件好事,因为让它原样挂在这里(未回答)对未来的访问者不是很有用。

标签: c++ nodes binary-search-tree treenode


【解决方案1】:

我最终想出的是:跟踪位于替换要删除节点的节点之上的父节点。然后将考虑两种情况:父节点是要删除的节点,父节点不是要删除的节点。通过在正确的情况下替换树的适当部分,树的结构和不变量保持正常,并且要删除的节点被成功删除。从技术上讲,这将是要删除的节点上的数据。

else if (toDelete->left != NULL && toDelete->right != NULL) {

    // find rightmost child on left that is a leaf
    Node* replacement = toDelete->left;
    parent = toDelete;
    // parent is now the parent of the replacement
    while ( replacement->right ) {
        parent = replacement;
        replacement = replacement->right;
    } // By the end, parent will be the node one above replacement

    toDelete->key = replacement->key;

    if (parent == target) 
        parent->left = replacement->left;
    else 
        parent->right = replacement->left;

    delete replacement;
    return true;
}

【讨论】:

    【解决方案2】:

    这就是我为使其正常工作所做的。只需检查该节点是否为根节点,如果是,则设置新根。下面是我的工作代码。用星号标记的三个地方是我添加的让它工作的地方。所有其他代码行只是标准的教科书理论。

    inline NamesBinaryTree::Node* NamesBinaryTree::Node::removeNode (Node*& node, const Female* female, stringComparisonFunction s) {  // Taken from p.253 of Alex Allain's "Jumping Into C++".
        if (!node)
            return nullptr;
        if (node->femaleInfo.first == female->getName()) {
            if (!node->left) {  // i.e. node has either one child or zero children.
                Node* rightNode = node->right;
                if (node->isRoot())  // ***
                    namesBinaryTree.setRoot(rightNode);  // Tested to work correctly.  Note that we cannot call 'delete node;', since that will delete the very root that we are setting!
                else
                    delete node;        
                return rightNode;  // This will return nullptr if node->right is also nullptr, which is what we would want to do anyway since that would mean that node has zero children.
            }
            if (!node->right) {  // i.e. node has exactly one child, namely its left child, in which case return that left child.
                Node* leftNode = node->left;
                if (node->isRoot())  // ***
                    namesBinaryTree.setRoot(leftNode);
                else
                    delete node;
                return leftNode;  // This will never be nullptr, else the previous if condition would have been met instead.
            }
            Node* maxNode = findMaxNode(node->left);  // node has two children, so it shall be replaced by the largest valued node in its left subtree.
            maxNode->left = removeMaxNode(node->left, maxNode);  // Note that maxNode->left = node->left is not enough because without actually removing maxNode, the node that was pointing to maxNode will now be pointing to maxNode in its new position (and the subtree under it), and the subtree that was under maxNode will now be gone.
            maxNode->right = node->right;
            if (node->isRoot())  // ***
                namesBinaryTree.setRoot(maxNode);  // Tested to work correctly.
            else
                delete node;
            return maxNode;
        }
        else {
            const int result = (*s)(female->getName(), node->femaleInfo.first); 
            if (result < 0) 
                node->left = removeNode(node->left, female, s);  // This assignment can only work if node is passed by reference (hence the parameter Node*& node), at least this is what "C++ Essentials" did in their solution, p.247.
            else  // Don't use 'else if (result > 0)'.  Let the equality case be covered here too (just as in NamesBinaryTree::Node::insertNode).
                node->right = removeNode(node->right, female, s);  // Again, this assignment can only work if node is passed by reference (hence the parameter Node*& node).
        }
        return node;  // So if node->femaleInfo.first != female->getName(), then the same node is returned, which means that the two assignment lines above don't change any values.
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多