c++ - 重新平衡时如何修复 AVL 删除操作中的段错误?

标签 c++ segmentation-fault avl-tree tree-balancing

我正在实现一个 AVL 树,我的搜索和插入功能正常工作,但我的删除功能出现段错误。我之前已经正确地实现了 BST 树,所以我知道问题在于树的重新平衡而不是节点的初始删除。

因为我的插入操作与重新平衡一起工作,所以我也知道问题不在于旋转函数本身。

我尝试了不同的策略,例如在每个节点上保持平衡因子,并尝试实现我在网上找到的其他源代码,但我总是以段错误告终,而且真的找不到位置。如果有任何帮助,我将不胜感激。

class AVL
{
public:
    AVL();

    Node* insert(int num);
    Node* search(int num);
    Node* remove(int num);
    void print();
    void comparisonPrint();

private:
    int comparisonCount;
    Node* root;
    int max(int a, int b);
    int getHeight(Node* t);
    int getBalance(Node* t);
    Node* insert(Node* &t, int num);
    Node* rotateWithLeftChild(Node* t);
    Node* rotateWithRightChild(Node* t);
    Node* doubleRotateWithLeftChild(Node* t);
    Node* doubleRotateWithRightChild(Node* t);

    Node* search(Node* t, int num);
    Node* removeMin(Node* parent, Node* node);
    Node* remove(Node* &t, int num);
    void print(Node* t);
    //print
};

int AVL::max(int a, int b)
{
    return (a > b)? a : b;
}
int AVL::getHeight(Node* t)
{
    return (t == NULL) ? 0 : t->height;
}
int AVL::getBalance(Node* t)
{
    if(t == NULL)
        return 0;
    return getHeight(t->leftChild) - getHeight(t->rightChild);
}

//helper function for remove - finds min
Node* AVL::removeMin(Node* parent, Node* node) //removes node, but does not delete - returns ptr instead
{
    if(node != NULL)
    {
        if(node->leftChild != NULL) //go to leftmost child in right subtree
            return removeMin(node, node->leftChild);
        else //min val
        {
            parent->leftChild = node->rightChild;
            return node;
        }
    }
    else //subtree empty - incorrect use of function
        return NULL;
}

Node* AVL::remove(Node* &t, int num)
{
    cout << num;
    if(t != NULL)
    {

        if(num > t->key)
        {
            comparisonCount++;
            remove(t->rightChild, num);
        }
        else if(num < t->key)
        {
            comparisonCount++;
            remove(t->leftChild, num);
        }
        else if(t->leftChild != NULL && t->rightChild != NULL)
        {
            comparisonCount++;
            //2 children
            Node* minRightSubtree = removeMin(t, t->rightChild);
            t->key = minRightSubtree->key;
            delete minRightSubtree;
        }
        else
        {
            comparisonCount++;
            //0 or 1 child
            Node* temp = t;
            if(t->leftChild != NULL)
                t = t->leftChild;
            else if(t->rightChild != NULL)
                t = t->rightChild;
            delete temp;
        }

        //update height
        t->height = max(getHeight(t->leftChild), getHeight(t->rightChild)) + 1;

        int balance = getBalance(t);

        if(balance > 1 && getBalance(t->leftChild) >= 0)
            return rotateWithRightChild(t);
        if(balance > 1 && getBalance(t->leftChild) < 0)
        {
            t->leftChild = rotateWithLeftChild(t->leftChild);
            return rotateWithRightChild(t);
        }
        if(balance < -1 && getBalance(t->rightChild) <= 0)
            return rotateWithLeftChild(t);
        if(balance < -1 && getBalance(t->rightChild) > 0)
        {
            t->rightChild = rotateWithRightChild(t->rightChild);
            return rotateWithLeftChild(t); 
        }

    }

    return t;
}

所以我需要 remove 函数来删除指定的节点并在必要时使用适当的旋转重新平衡树。但是,每当我尝试在我的 main 中调用该函数时,我都会遇到段错误。谢谢。

最佳答案

您的代码有两个问题。首先是 removeMin 函数和 remove 函数中的 else if 部分,当要删除的节点有两个子节点时。

removeMin 函数的基本目标应该是找到要删除的节点的有序后继节点,在您的例子中是 t。考虑 t 有 2 个子节点(均为叶节点)的情况,然后您的 removeMin 函数会将 t->leftChild 设置为 t->rightChild->rightChildNULL 这是错误的。此外,树的重组应该在 remove 内完成,因此 removeMin 变为:

Node* AVL::removeMin(Node* node) // returns inorder successor of 't'
{
    if(node->left == NULL)
        return node;
    return removeMin(node->left);
}

来到remove函数,我们用minRightSubtree->key重置t->key,现在要删除的节点是minRightSubtree。但是请注意,从节点 t 到节点 minRightSubtree,键的顺序在链中发生了变化。 t->key小于minRightSubtree之前所有节点的key。因此,您不能只删除 minRightSubtree,您必须在节点 minRightSubtree 上调用 remove 函数,这将负责重组此链。此外,您还可以从递归堆栈中获得一些帮助,以便在删除/旋转后为当前节点 t 获取正确的子节点:

Node* AVL::remove(Node* &t, int num)
{
    if (t == NULL)
        return NULL;

    if (num > t->key)
        t->rightChild = remove(t->rightChild, num);
    else if (num < t->key)
        t->leftChild = remove(t->leftChild, num);
    else if (t->leftChild != NULL && t->rightChild != NULL)
    {
        //2 children
        Node* minRightSubtree = removeMin(t->rightChild);
        t->key = minRightSubtree->key;
        t->rightChild = remove(t->rightChild, minRightSubtree->key);
    }
    else
    {
        //0 or 1 child
        Node* temp = t;
        if (t->leftChild != NULL)
            t = t->leftChild;
        else if (t->rightChild != NULL)
            t = t->rightChild;

        if(temp == t)
            t = NULL;
        delete temp;
    }

    if (t == NULL)          // this case was added since there is a possibility of deleting 't'
        return NULL;

    //update height
    t->height = max(getHeight(t->leftChild), getHeight(t->rightChild)) + 1;

    int balance = getBalance(t);

    if (balance > 1 && getBalance(t->leftChild) >= 0)
        return rotateWithRightChild(t);
    if (balance > 1 && getBalance(t->leftChild) < 0)
    {
        t->leftChild = rotateWithLeftChild(t->leftChild);
        return rotateWithRightChild(t);
    }
    if (balance < -1 && getBalance(t->rightChild) <= 0)
        return rotateWithLeftChild(t);
    if (balance < -1 && getBalance(t->rightChild) > 0)
    {
        t->rightChild = rotateWithRightChild(t->rightChild);
        return rotateWithLeftChild(t);
    }
    return t;
}

我假设您更新高度和平衡根子树的代码是正确的,因为我忘记了它的理论并且需要修改。

关于c++ - 重新平衡时如何修复 AVL 删除操作中的段错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58458526/

相关文章:

c++ - Windows 上不会发生的 Linux 上的隐形 SIGSEGV?

java - 为什么这个方法会导致无限递归调用?

c - C 中的二叉搜索树插入函数

字节不在文件中的 Python Unicode 解码错误

c++ - 如何推断迭代器模板类型或其模板的嵌套类型?

c++ - 如何在 Win32 中创建带位图的组合框?

c++ - noexcept 没有被腐烂去除

c++ - 共享指针的段错误

c - GCC:段错误和调试程序仅在优化时崩溃

c++ - AVL树中节点的平衡因子