【问题标题】:Access violation. RB tree not creating node correctly访问冲突。 RB 树没有正确创建节点
【发布时间】:2015-10-09 17:59:40
【问题描述】:

我有一个 RB 树。它正确地将第一个节点插入到根节点,但是在调试器上单步执行时它不会创建下一个节点,并且在尝试设置父指针时会导致访问冲突,因为它未能创建其父节点指针所在的节点试图设置。

源代码:

core.h

#include <iostream>
#ifndef CORE_H_
#define CORE_H_

struct node {
public:
    node(double);
    bool color; //false = red, true = black
    double key_value;
    node *left_child;
    node *right_child;
    node *parent;
};

class red_black_tree {
public:
    red_black_tree();
    ~red_black_tree();
    void print_tree();
    void insert(double key);
//  node *search(double key);
//  void delete_leaf(double key);
private:
    node *root;
    node *get_uncle(node *);
    node *get_grand(node *);
    void insert_case1(node *);
    void insert_case2(node *);
    void insert_case3(node *);
    void insert_case4(node *);
    void insert_case5(node *);
    void rotate_left(node *);
    void rotate_right(node *);
    void insert(node *leaf, double key);
    //node *search(node *leaf, double key);
//  void delete_leaf(double key);
    void print_tree(node *leaf);
    void destroy_tree(node *leaf);


};
#endif

core.cpp

#include "core.h"
#include <iostream>
node::node(double key) {
    left_child = right_child = parent = nullptr;
    color = false;
    key_value = key;
}

red_black_tree::red_black_tree() {
    root = nullptr;
}

red_black_tree::~red_black_tree() {
    if (root != nullptr) {
        destroy_tree(root);
    }

}

void red_black_tree::destroy_tree(node *leaf) {
    if (leaf->left_child!= nullptr) {
        destroy_tree(leaf->left_child);
    }
    if (leaf->left_child != nullptr) {
        destroy_tree(leaf->right_child);
    }
    delete leaf;
}

node *red_black_tree::get_grand(node *leaf) {
    if (leaf->parent != nullptr && leaf->parent->parent != nullptr)
        return leaf->parent->parent;
    else return nullptr;
}

node *red_black_tree::get_uncle(node *leaf) {
    node *g = get_grand(leaf);
    if (g == nullptr)
        return nullptr;
    if (leaf->parent == g->left_child)
        return g->right_child;
    else return g->left_child;
}

void red_black_tree::insert(double key) {
    if (root == nullptr) {
        root = new node(key);
        insert_case1(root);
    }
    else insert(root, key);
}


void red_black_tree::insert(node *leaf, double key) {
    //normal recursive binary tree insertion
    if (leaf == nullptr) {
        leaf = new node(key);
    }
    else if (key < leaf->key_value) {
        insert(leaf->left_child, key);
        leaf->left_child->parent = leaf;
        insert_case1(leaf);
    }
    else if (key >= leaf->key_value) {
        insert(leaf->right_child, key);
        leaf->right_child->parent = leaf;
        insert_case1(leaf);
    }

}

void red_black_tree::rotate_left(node *leaf) {
    node *grand = get_grand(leaf), *s_parent = grand->left_child, *left = leaf->left_child;
    grand->left_child = leaf;
    leaf->left_child = s_parent;
    s_parent->right_child = left;
    s_parent->parent = leaf;
    leaf->parent = grand;
}

void red_black_tree::rotate_right(node *leaf) {
    node *grand = get_grand(leaf), *s_parent = grand->right_child, *right = leaf->right_child;
    grand->right_child = leaf;
    leaf->right_child = s_parent;
    s_parent->left_child = right;
    s_parent->parent = leaf;
    leaf->parent = grand;
}

void red_black_tree::insert_case1(node * leaf) {
    if (leaf->parent == nullptr) {
        leaf->color = true;
    }
    else {
        insert_case2(leaf);
    }

}

void red_black_tree::insert_case2(node *leaf) {
    if (leaf->parent->color == true) {
        return;
    }
    else
        insert_case3(leaf);
}

void red_black_tree::insert_case3(node *leaf) {
    node *uncle = get_uncle(leaf), *grand;
    if ((uncle != nullptr) && (uncle->color == false)) {
        leaf->parent->color = true;
        uncle->color = true;
        grand = get_grand(leaf);
        grand->color = false;
        insert_case1(grand);
    }
    else {
        insert_case4(leaf);
    }
}

void red_black_tree::insert_case4(node *leaf) {
    node *grand = get_grand(leaf);
    if ((leaf == leaf->parent->right_child) && (leaf->parent == grand->left_child)) {
        rotate_left(leaf);
        leaf = leaf->left_child;
    }
    else if ((leaf == leaf->parent->left_child) && (leaf->parent == grand->right_child)) {
        rotate_right(leaf);
        leaf = leaf->right_child;
    }
    insert_case5(leaf);
}

void red_black_tree::insert_case5(node *leaf) {
    node *grand = get_grand(leaf);
    leaf->parent->color = true;
    grand->color = false;
    if (leaf == leaf->parent->right_child) {
        rotate_right(grand);
    }
    else
        rotate_left(grand);
}

void red_black_tree::print_tree() {
    print_tree(this->root);
}

void red_black_tree::print_tree(node *leaf) {
    if (leaf != nullptr) {
        print_tree(leaf->left_child);
        std::cout << leaf->key_value;
        print_tree(leaf->right_child);
    }
}

main.cpp

#include "core.h"

int main() {
    red_black_tree tree;
    tree.insert(10);
    tree.insert(5);
    tree.insert(15);
    tree.print_tree();
    system("pause");
}

好的,所以在示例main()中,第一次插入tree.insert(10)有效,它调用非递归插入,确定根为NULL,然后将根指针设置为带有键的新节点10.第二个tree.insert(5),调用非递归insert,判断根存在,然后调用递归insertinsert(root, 5)。这确定根不是NULL,然后转到第一个if/else 语句,并像insert(root-&gt;left_child, 5) 一样递归调用insert。这将确定root-&gt;left_childNULL 并在root-&gt;left_child 的地址处创建一个新节点。然后该函数返回到原始插入调用并尝试将左孩子的父母设置为根,导致访问冲突,因为不知何故未设置左孩子。为什么插入函数没有设置节点,我的调试器说,当我单步执行时,节点是使用leaf = new node(5) 在递归的最后一级创建的,但是当它退出一个级别时,节点仍然是NULL那个地址?

【问题讨论】:

  • gdb 是你的朋友。
  • 赋值给一个参数不会改变你传入的变量的值。
  • 我没看懂,我没有传变量作为参数,我把指针传给了根节点的左孩子的地址,
  • 如果我有一个带有指向另一个相同类型结构的指针的结构,struct node{node *other;} 然后创建一个对象node * foo 并执行foo-&gt;other = new node,这将起作用,这基本上就是我在这里做。
  • @jordan 'leaf = new node(key);'

标签: c++ recursion data-structures


【解决方案1】:

看看你的这部分代码:

void red_black_tree::insert(node *leaf, double key) {
    //normal recursive binary tree insertion
    if (leaf == nullptr) {
        leaf = new node(key);
    }
    else if (key < leaf->key_value) {
        insert(leaf->left_child, key);
        leaf->left_child->parent = leaf;
        insert_case1(leaf);
    } ...

主要的insert(5) 会发生什么? 第二个if条件成功,所以调用insert(leaf-&gt;left_child, key),但是由于这里的叶子是根,它还没有孩子,所以我们再次进入同样的方法,叶子=nullptr。当leaf == nullptr(第一个 if 条件)时,您只需创建一个节点,但不会将其附加到树的任何位置。

这解释了为什么你有这种模式。现在树创建的整个过程需要重新思考。问题是几乎所有节点都有一个非空的左右子节点。

【讨论】:

  • 这就是我感到困惑的地方,因为当它调用insert(leaf-&gt;left_child, key)时,叶子是根;但是在新的函数调用中,leaf 现在表示root-&gt;left_child,那么new 不应该在root-&gt;left_child 的地址处创建一个新节点吗?我只是不明白:/
  • @Jordan,看,基本上,您没有遵循 OOP 方法。我建议您将插入方法移至节点类本身。然后该节点检查其左右子节点。如果它看到新键必须在左子树上,它会查看 left_child 是否为空。如果是这样,它会创建节点并将其附加为 left_child。如果没有,它会递归地传递给 left_child 的新键......你会看到逻辑。
  • 问题在于插入案例。它们需要由外部树类控制,因为根节点可以根据插入和删除而改变;我必须通过手动创建根节点然后调用插入函数来创建树。通过范围界定,我将如何访问新的根节点,因为我手动创建的根指针仍将引用旧节点,只是一个不再是根的节点?
  • @Jordan 是的,这是您需要处理的事情。但是由于您有父链接,您始终可以检查旧根并通过父指针导航以检索根。这是一种解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-27
  • 2012-05-15
相关资源
最近更新 更多