List All Function仍尝试检索从二叉搜索树中删除的节点

时间:2015-11-25 04:19:00

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

我有这些函数从我的二叉搜索树中删除一个节点:

bool collection::removeFromTree(const char name[])
{
    for (treeNode * curr = root; curr;)
    {
        int8_t result = strcmp(name, curr->item->getName());
        if (result == 0)
        {
            deleteNode(curr);
            return true;
        }
        else if (result < 0)
            curr = curr->left;
        else if (result > 0)
            curr = curr->right;
    }
    return false;
}
void collection::deleteNode(treeNode *& goneNode)
{
    //if it's a leaf
    if (!goneNode->left && !goneNode->right)
    {
        delete goneNode; //node's destructor gets invoked
        goneNode = nullptr;
    }
    //if it has right child
    else if (!goneNode->left)
    {
        goneNode = goneNode->right;
    }
    //if it has left child
    else if (!goneNode->right)
    {
        goneNode = goneNode->left;
    }
    //if it has both children
    else
    {
        treeNode * prev = nullptr;
        treeNode * curr = goneNode->right;
        while (curr->left)
        {
            prev = curr;
            curr = curr->left;
        }
        //prev points to the copy over data
        delete goneNode->item;
        if (!prev)
        {
            goneNode->item = curr->item;
            goneNode->right = curr->right;
            curr->item = nullptr;
        }
        else
        {
            goneNode->item = curr->item;
            curr->item = nullptr;
            prev->left = curr->right;
        }
    }
}

这样运行正常,但是当我尝试在删除节点后使用这些函数列出树中的所有元素时:

void collection::displayByName() const
{
    std::cout << std::endl
        << "========================================" << std::endl;
    //display the tree inorder
    listAll(root);
}
void collection::listAll(const treeNode * const & root) const
{
    if (root)
    {
        std::cout << *(root->item) << std::endl
            << "========================================" << std::endl;
        listAll(root->left);
        listAll(root->right);
    }
}

我收到此错误:enter image description here

当我在删除节点(调用这些析构函数)后退出程序时:

collection::~collection()
{
    delete root;
}
collection::treeNode::~treeNode()
{
    delete left;
    delete right;
}

我收到此错误:enter image description here

我们非常感谢任何建议,因为我认为listAll()函数没有理由调用我已删除的节点。

顺便说一句,这是我的treeNode结构:

struct treeNode
    {
        treeNode();
        treeNode(vendor *& item);
        ~treeNode();
        vendor * item;
        treeNode *left, *right;
    };
    treeNode * root;    //the bst

    hashNode ** table;  //the hash table
    uint8_t capacity;
    uint8_t size;
    const static uint8_t INIT_CAP = 20;

1 个答案:

答案 0 :(得分:1)

当您需要从单个链接列表或树中删除节点时,我发现使用指针指针很方便。也就是说,如果我们有treeNode** ptr;,则*ptr是指向我们节点的指针。因此,如果ptr = &root,则*ptr = nullptrroot设置为nullptr

我删除了deleteNode函数并将其逻辑放在removeFromTree函数中。

bool collection::removeFromTree(const char name[])
{
    treeNode** ptr = &root;

treeNode不是指向ptr的指针,而是指向树结构中的treeNode*。这样,我们可以修改引导我们到当前节点的指针。标记为//same as before的行具有与您使用的逻辑相同的逻辑,只是可能已修改以解释ptr具有另一级解除引用的事实。

    int result; //same as before
    while (*ptr) //While we haven't hit a dead end
    {
        result = strcmp(name, (*ptr)->item->getName()); //same as before
        if (result < 0) //same as before
            ptr = &((*ptr)->left); //same as before
        else if (result > 0) //same as before
            ptr = &((*ptr)->right); //same as before
        else //begin deleteNode() logic
        {
            if ((*ptr)->left && (*ptr)->right) //two children
            {

这里,我们使用指向成员的指针,因为替代是每一行的条件运算符。如果一个节点有两个子节点,我们需要在左侧找到最右边的节点,或者在右边找到最左边的节点。这是我们可以用。替换当前节点的节点。

                treeNode* treeNode::*dir = some_condition ? &treeNode::right : &treeNode::left; //pointer to treeNode member of type treeNode*
                treeNode* treeNode::*ndir = some_condition ? &treeNode::left : &treeNode::right;  //pointer to treeNode member of type treeNode*

dir现在指向leftright,这是我们搜索树的方向。 ndir是相反的方向。因此,如果我们想要左侧最右边的节点(*ptr)->*dir == (*ptr)->left(*ptr->*ndir == (*ptr)->right。如果我们想要最左边的节点,它将被颠倒。实际上,这只是一种更简单的工作方式。它应该不难删除。 some_condition只是truefalsetrue表示树的左侧(来自当前节点)丢失节点,而false表示右侧。

                treeNode** replacement = &((*ptr)->*ndir); //the node to replace the current one with
                while ((*replacement)->*dir) //While we aren't at the edge
                    replacement = &((*replacement)->*dir);

这循环直到*replacement是我们需要用{。}替换*ptr的节点。

                treeNode* rep_branch = (*replacement)->*ndir; //If the replacement node had a child, this is now it

                (*replacement)->left = (*ptr)->left; //Copy current to replacement
                (*replacement)->right = (*ptr)->right; //Copy current to replacement
                (*ptr)->left = nullptr; //null out current in case of destructor
                (*ptr)->right = nullptr; //null out current in case of destructor

现在,替换节点指向要删除的节点的子节点,我们即将过期的节点已经没有子节点了。现在,删除不需要的节点是安全的。如果节点类有一个析构函数来删除其子节点,那么leftright指针设置为nullptr以防万一。

                delete *ptr; //delete unwanted node
                *ptr = *replacement; //replacement node has taken the unwanted node's place in the tree
                *replacement = rep_branch; //The replacement's child takes its own place
            }

这样就完成了树的结构。无用节点在哪里,替换节点取而代之。并且因为替换节点需要是边缘节点,所以它最多只有一个子节点。我们只是将其替换为孩子。

            else if ((*ptr)->left) //one child on left
            {
                treeNode* current = *ptr;
                *ptr = (*ptr)->left; //replace current with left
                current->left = nullptr; //null out for safety
                delete current;
            }
            else if ((*ptr)->right) //one child on right
            {
                treeNode* current = *ptr;
                *ptr = (*ptr)->right; //replace current with right
                current->right = nullptr; //null out for safety
                delete current;
            }
            else //no children
            {
                delete *ptr;
                *ptr = nullptr;
            }
            return true; //yay it's over
        }
    }
    return false; //never found it
}

其余的相当简单,只需更换更简单的节点并返回。希望这能为您提供一些关于如何处理这类问题以及偶尔使用其中一些结构的想法。这就是我对treeNode**使用treeNode*进行此类操作的意思。