【问题标题】:Segfault when accessing nodes of binary search tree访问二叉搜索树节点时的段错误
【发布时间】:2019-10-28 20:31:59
【问题描述】:

我在 C 中使用以下结构实现一个简单的二叉搜索树:

typedef struct bstNode{
    double val;
    struct bstNode* left;
    struct bstNode* right; 
} Node;

typedef struct bsTree{
    Node* root;
    size_t size;
} BST;

其中BST* createTree(double val) 返回具有以下例程的二叉搜索树结构:

BST* createTree(double val){
    Node* root = (Node*) malloc(sizeof(Node));
    root->val = val;
    root->right = NULL;
    root->left = NULL;
    BST* t = malloc(sizeof(BST));
    if(t == NULL){
        free(root);
        free(t);
        return NULL;
    }
    t->root = root;
    t->size = 1;
    return t;
}

此外,我还实现了int insert(BST* tree, double in),它将给定值作为节点插入到二叉搜索树中:

int insert(BST* tree, double in){
    if(tree->root == NULL)  return -1;
    Node* curr = tree->root;

    while(curr!=NULL){
        if(curr->val<=in){
            curr = curr->right;
        }else{
            curr = curr->left;
        }
    }

    curr = (Node*) malloc(sizeof(Node));
    if(curr == NULL){
        printf("Could not create new node");
    }
    curr->val = in;
    curr->right = NULL;
    curr->left = NULL;
    tree->size = tree->size + 1;
    return 0;
}

现在,如果我使用以下代码测试实现:

int main(){
    BST* tree = createTree(1.0);
    if(tree==NULL){
        printf("Tree could not be created");
        return -1;
    }

    insert(tree, 2.0);
    printf("%lf\n", tree->root->right->val);
    destroyTree(tree);

    return 0;
}

即使我在insert() 函数中成功创建了curr,我也会遇到分段错误。我尝试了各种方法,例如已经提前分配了 root 的左右节点(在createTree() 方法中),但这也不起作用。我在这里做错了什么?

【问题讨论】:

  • 在插入函数中,您正在分配一个新节点(curr),但您永远不会将它链接到树的其余部分。
  • @jmq 我可以问为什么当我们遍历树时它没有链接到树的其余部分? Curr 是树中某个节点的子节点,因为 curr = curr->right 或 curr->left。
  • 您正在遍历树,但您正在丢弃结果。我不确定你想如何将它附加到树上。现在在while循环之后curr将为NULL。 while 循环后的 curr 值无关紧要,因为无论如何你都在覆盖它。
  • @jmq 阅读以下答案后,我明白你的意思。 Curr 被 malloc() 覆盖,我们必须将其链接到父级。感谢您的评论

标签: c segmentation-fault


【解决方案1】:

insert 内新创建的 Node 未附加到您的树。

curr是局部变量,当你用curr = (Node*)malloc(sizeof(Node))修改它时,它只是改变了这个指针的值,但是树中没有一个节点会指向这个值。

解决此问题的简单方法是创建指向节点“父”的临时指针,并添加一个布尔变量,该变量指示应将父新子指针的哪个节点(左或右)分配。

int insert(BST* tree, double in){
    if(tree->root == NULL)  return -1;
    Node* curr = tree->root;

    Node* parent = NULL;      // added
    bool addToRight = false;  // added
    while(curr!=NULL){
        parent = curr;
        if(curr->val<=in){
            curr = curr->right;
            addToRight = true; // added
        }else{
            curr = curr->left;
            addToRight = false; // added 
        }
    }

    curr = (Node*) malloc(sizeof(Node));
    if(curr == NULL){
        printf("Could not create new node");
    }
    curr->val = in;
    curr->right = NULL;
    curr->left = NULL;

    if (addToRight)             // added
        parent->right = curr;
    else 
        parent->left = curr;

    tree->size = tree->size + 1;
    return 0;
}

另一种解决方案是使用Node**。通过这种方式,您可以存储叶子(左或右)的地址并将其设置为从malloc 返回的指针的值。

Node** t = NULL; // added
while(curr!=NULL){
    if(curr->val<=in){
        curr = curr->right;
        t = &(curr->right); // added
    }else{
        curr = curr->left;
        t = &(curr->left); // added
    }
}

curr = (Node*) malloc(sizeof(Node));
if(curr == NULL){
    printf("Could not create new node");
}
*t = curr; // added
curr->val = in;
curr->right = NULL;
curr->left = NULL;

【讨论】:

  • 谢谢!我以为我malloc之后父节点还是会指向curr,但显然这不是真的。
  • 如果你有Node* ptr by ptr = ... 你只能设置pointee,即指针指向的值。但是要更改指针本身的值,您需要始终使用指向指针的指针 - Node** ptr,因此在 *ptr = ... 中,您更改指针占用的内存单元的值 - 这是您想要实现的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多