【问题标题】:How to fix fringe cases in BST delete function?如何修复 BST 删除功能中的边缘情况?
【发布时间】:2010-11-14 02:02:26
【问题描述】:

假设我的删除尝试重新平衡树的顺序(从左到右)。

我目前正在编写一个 BinarySearchTree 类,并且我的删除功能目前在大多数情况下都有效(我相信 - 我希望

  • 删除根节点不起作用,因为它没有父节点。
  • 在 next 有两个孩子的最后一种情况下,如果它最左边的最右边的后代是右孩子本身,那么 newCurr 将指向 next,这将导致问题,因为 newCurr(又名 next)最终会与 New 交换。

根删除的建议解决方案:

  1. 我的解决方案是删除根并使用我的 BST 类的成员函数将根设置为新(使该节点成为根),然后将新的位置设置为 0/NULL。

    A:这行得通,但我必须把案例工作放在所有地方。

  2. 在 BST 类中有一个虚拟父节点,它只是将根节点作为它的右手对象(由 billyswong 建议)。

    A:这可能行得通,但我觉得我应该有特殊情况的工作来处理它。

删除两个孩子的建议解决方案:

  1. 我的解决方法,就是暂时存储New的位置,将New在其父节点的位置设置为New的右孩子,然后删除临时指针。

    A:这会起作用,但它没有我想象的那么优雅。

  2. “旋转节点”(不知道该怎么做,由 Agor 建议)。

这是我的代码:

if (root()->value() == val)
{
    delete root();
    this->setRoot(New);
    newCurr->setLeft(0);
}    
else if (newCurr == next)
{
    Node *temp = New;
    newCurr->setRight(New->right());
    delete temp;
} 

请发帖者告诉我此代码 1) 是否有效 2) 是否最佳。

编辑:很抱歉我在函数末尾处使用的驼峰帽不一致。我想不出更好的变量名描述,但new 是 C++ 中定义的关键字。

Edit2:发布重构代码,但错误仍然存​​在。

void BinarySearchTree::del(int val)
{
//curr refers to the parent of next
//next is the node we will want to delete
    Node* curr = 0;
    Node* next = root(); 

//these will only be used if you get into
//the last case, where you have two children
//on next
    Node* newCurr = curr;
    Node* New = next;

//boolean value to check if you found the value
//in the binary search tree
    bool found;

//set curr and next; will be false if val not found
    found = setCurrAndNext(val, curr, next);


//get next to the node needing deletion
//and set curr to its parent
//pass by ref function  
//return value is that you found it  
    if (found)
    {
        setNewCurrAndNew (newCurr, New);
    }   
    else
    { 
        return;    
    }

/* next is a leaf */

    if (nextIsLeaf(next))
    {
        handleLeafCase(curr, next);
        return;
    }   
/* next has a single child */        
    else if (nextHasSingleChild(next))       
    {
        if(leftIsChild(next))  
        {
            handleLeftCase(curr, next);
        }
        else
        {
            handleRightCase(curr, next);
        }
    }
/* next has two children */    
    else
    {  
       if (newCurr == next)
       {
            Node *temp = New;
            newCurr->setRight(New->right());
            delete temp;
       } 
       else if (next == curr->left())
       {
            if (New == newCurr->left())
            {
               curr->setLeft(New);
               newCurr->setLeft(next);
            }
            else
            {
               curr->setLeft(New);
               newCurr->setRight(next);
            }
       }
       else
       {
            if (New == newCurr->left())
            {
               curr->setRight(New);
               newCurr->setLeft(next);
            }    
            else
            {
               curr->setRight(New);
               newCurr->setRight(next);
            }
       }

       if (next->left() == 0 && next->right() == 0)
       {
            newCurr->setRight(0);
            newCurr->setLeft(0);

            delete next;
       }
       else
       {
            if (next->left() == 0)
            {
               newCurr->setRight(next->left());
            }
            else
            {
               newCurr->setLeft(next->right());
            }

               delete next;
            }
        }
    }
}

【问题讨论】:

    标签: c++ binary-tree


    【解决方案1】:

    删除根节点不起作用,因为它没有父节点

    我对此的想法是,给你的根一个虚拟的父节点,一个不包含实际有用值但根作为其右孩子的节点。然后每个可删除节点现在都将有它们的父节点,并且 root() 将能够像其他节点一样被更统一地处理。那么你就不需要特殊的方法来记住树是否也是空的。

    编辑:found = setCurrAndNext(val, curr, next); 如何在外面设置currnext? AFAIK C/C++ 总是按值传递。我在这些辅助函数中发现了一些问题。

    【讨论】:

    • 好的,这将如何实现?我的 BST 对象会有 Node *rootParentDummy 或类似的东西吗?
    • 是的,类似的。实际上,您的代码还需要处理在 BST 中找不到您要删除的内容的情况,即 next == 0。回到主题,在我的建议中,一棵空树将只有一个 Node *parentSeed (甚至节点 parentSeed,如果你喜欢),其子节点设置为 null。然后当然Node* curr = 0;在你的方法开始将变成Node* curr = parentSeed;
    • 我在我的重构代码中修复了这个基本情况。如果 !found,则返回。
    • 那些辅助函数是通过引用传递的。指针语法减去指针。
    • 哦,抱歉,我没有注意到 Node 是一个类。我基本上是一个 C 人,在将声明视为它的指针时,我会自动将 Node 视为结构/对象。
    【解决方案2】:

    我不能给你代码或任何东西,但你正在寻找的是一个“旋转”运算符。基本上,它处理的是“烦人”的删除情况——当你删除一棵树时,它有 2 个子节点,它们都有 2 个子节点,比如根,还有树中的任何其他节点。

    rotate 所做的基本上是在一个非常局部的区域中,在相关节点之间交换子节点(我知道这会造成伤害),以便保持子节点的顺序(左侧较小,右侧较大)。类似于优先队列的“冒泡”功能。

    这是维基百科 (http://en.wikipedia.org/wiki/Tree_rotation) 页面。

    希望这能让你走上正轨!

    编辑以回应评论: 对不起,我应该解释的。当我说“树”时,我的意思是节点,维基百科页面应该仍然有用。由于二叉搜索树的数学递归定义,您可以将每个节点视为其自己的子树,因此我的初始语句(保持不变)。但这仅与您的问题无关,因此请继续... :)

    【讨论】:

    • 我不想删除树,我想删除某个节点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2022-09-28
    • 2012-12-25
    • 1970-01-01
    • 2023-02-25
    相关资源
    最近更新 更多