【问题标题】:Red Black Tree - deletion红黑树-删除
【发布时间】:2023-06-24 08:39:02
【问题描述】:

我已经为 RBT 实现了删除功能(基于 Cormen),它看起来很有效,但是在预购中测试删除 + 打印树给了我错误的答案。我花了几个小时寻找可能出了什么问题,但找不到任何东西......

这是按顺序打印树的函数:

void print_out(rbt_node *root, rbt_node *NIL)
{
    if(root != NIL)
    {
        printf("%d %s ", root->key, root->data.c_str());
        if(root->color == BLACK)
            printf("black ");
        else
            printf("red ");
        if(root->parent != NIL)
            printf("%d ",root->parent->key);
        else
            printf("- ");
        if(root->left != NIL)
            printf("%d ",root->left->key);
        else
            printf("- ");
        if(root->right != NIL)
            printf("%d ",root->right->key);
        else
            printf("- ");
        printf("\n");

        print_out(root->left, NIL);
        if(root->right != NIL)
        {
            print_out(root->right, NIL);
        }
    }
}

以下是要删除的其余重要内容:

rbt_node *NIL = new rbt_node;
NIL->color = BLACK;
NIL->left = NIL->parent = NIL->right = NIL;

rbt_node *tree_minimum(rbt_node *node, rbt_node *NIL)
{
    while(node->left != NIL)
        node = node->left;

    return node;
}

rbt_node *tree_succesor(rbt_node *node, rbt_node *NIL)
{
    if(node->right != NIL)
        return tree_minimum(node->right, NIL);

    rbt_node *helper = node->parent;
    while(helper != NIL && node == helper->right)
    {
        node = helper;
        helper = helper->parent;
    }

    return helper;
}

void delete_fixup(rbt_node *&root, rbt_node *&target, rbt_node *NIL)
{
    rbt_node *helper = NIL;
    while(target != root && target->color == BLACK)
    {
        if(target == target->parent->left)
        {
            helper = target->parent->right;
            if(helper->color == RED)
            {
                helper->color = BLACK;
                target->parent->color = RED;
                left_rotate(root, target->parent, NIL);
                helper = target->parent->right;
            }
            if(helper->left->color == BLACK && helper->right->color == BLACK)
            {
                helper->color = RED;
                target = target->parent;
            }
            else if(helper->right->color== BLACK)
            {
                helper->left->color = BLACK;
                helper->color = RED;
                right_rotate(root, helper, NIL);
                helper = target->parent->right;
            }
            else
            {
                helper->color = target->parent->color;
                target->parent->color = BLACK;
                helper->right->color = BLACK;
                left_rotate(root, target->parent, NIL);
                target = root;
            }
        }
        else
        {
            helper = target->parent->left;
            if(helper->color == RED)
            {
                helper->color = BLACK;
                target->parent->color = RED;
                right_rotate(root, target->parent, NIL);
                helper = target->parent->left;
            }
            if(helper->right->color == BLACK && helper->left->color == BLACK)
            {
                helper->color = RED;
                target = target->parent;
            }
            else if(helper->left->color== BLACK)
            {
                helper->right->color = BLACK;
                helper->color = RED;
                left_rotate(root, helper, NIL);
                helper = target->parent->left;
            }
            else
            {
                helper->color = target->parent->color;
                target->parent->color = BLACK;
                helper->left->color = BLACK;
                right_rotate(root, target->parent, NIL);
                target = root;
            }
        }
    }

    target->color = BLACK;
}

void rbt_delete(rbt_node *&root, int key, rbt_node *NIL)
{
    rbt_node *victim = to_delete(root, key, NIL);
    if(victim != NIL)
    {
        rbt_node *help_one = NIL;
        rbt_node *help_two = NIL;
        if(victim->left == NIL || victim->right == NIL)
            help_one = victim;
        else
            help_one = tree_succesor(victim, NIL);

        if(help_one->left != NIL)
            help_two = help_one->left;
        else
            help_two = help_one->right;

        help_two->parent = help_one->parent;

        if(help_one->parent == NIL)
            root = help_two;
        else if(help_one == help_one->parent->left)
            help_one->parent->left = help_two;
        else
            help_two->parent->right = help_two;

        if(help_one != victim)
        {
            victim->key = help_one->key;
            victim->data = help_one->data;
        }

        if(help_one->color == BLACK)
            delete_fixup(root, help_two, NIL);
    }

    root->color = BLACK;
}

【问题讨论】:

  • 一天?你在开玩笑吧:D?好吧,你错了。其次,我提供源代码(我的源代码),我只需要帮助来捕捉错误,我可能会在花了几个小时检查后找不到。
  • 出了什么问题?数据是什么,你期望什么,你得到什么?
  • speedyshare.com/files/28410102/tests.rar 那是测试+我的输出(它有点大),删除第三个值并打印出树后出现问题(第 277 行错误,然后几行错误,然后似乎又好了...)

标签: c++ red-black-tree


【解决方案1】:

至少在 IMO,您的 print_out 并没有真正发挥应有的作用。具体问题是它试图(直接)打印出子节点的数据。

当您处理二叉树(任何类型的——不平衡、AVL、RB 等)时,遍历通常大致如下所示:

void print_out(node *current_node) { 
    if current_node != NIL {
        show_data(current_node);

        print_out(current_node->left);
        print_out(current_node->right);
   }
}

按原样,这是预购;与对print_out 的递归调用相比,后序或有序只是重新排列print_data 的问题(对于后序,它在它们之后,对于它们之间的顺序)。

然而,特别是 print_out 不应该与左子树或右子树有任何关系,除了在递归调用中将它们作为参数传递。它通常也不应该检查它们是否为 NIL/NULL——它应该只进行递归调用,并让递归调用中的 if (current_node != NIL) 句柄处理我们遇到叶节点的可能性。

如果你愿意,可以称我为懒惰,但这大约是我愿意在没有关于我正在寻找什么样的问题的至少 一些 指导的情况下检查的内容,并且至少是合理的保证它是正确的地方。如果您展示了可以插入几个节点并获得预期的结构,然后在删除节点时显示 what 出错,这将很有帮助(例如)。节点还在吗?其他节点会丢失吗?所有节点都对,但平衡不对?如果是这样,天平出了什么问题?

【讨论】:

  • 你的方法和我的完全一样,这不是问题,关于指导,我想我可以帮忙解决这个问题:pastebin.com/GMaqGQsf 什么都没有丢失,但此时平衡似乎是错误的。后来有更多错误的行,节点也没有预期的颜色(但它仍然是有效的树)。如果需要,我可以提供完整的源代码。