【问题标题】:Binary Tree implementation: issues with insert_tree()二叉树实现:insert_tree() 的问题
【发布时间】:2020-08-25 05:32:51
【问题描述】:

在我学习 C++ 之后,我开始学习一点点 C,所以我使用的是 bintree 实现。

代码:

struct Node {
    int value = -1;
    struct Node* left = NULL;
    struct Node* right = NULL;
};

void insert_tree(int value, Node* root) {
    if (root == NULL) {
        root = (struct Node*) malloc(sizeof(Node));
        root->value = value;
        printf("%d\n", root->value);
        root->left = NULL;
        root->right = NULL;
    }
    else {
        if (root->value >= value) {
            insert_tree(value, root->left);
        }
        else {
            insert_tree(value, root->right);
        }
    }
}

void printTree(Node* root) {
    if (root != NULL) {
        printf("%i\n", root->value);
        printTree(root->left);
        printTree(root->right);
    }
}

int main() {
    Node* root = NULL;
    insert_tree(30, root);
    printTree(root);
}

在这里我可以看到 insert_tree 如何执行 malloc 并正确分配内存,因此它在 insert_tree 内输出 30,但是当我调用 printTree() 时,树中没有节点。我不知道为什么,因为我传递了节点的左指针,并且它应该不受insert_tree 的上下文的影响。

insert_tree() 这里有什么问题?

【问题讨论】:

    标签: c pointers struct binary-search-tree pass-by-reference


    【解决方案1】:

    对于初学者这个结构声明

    struct Node {
        int value = -1;
        struct Node* left = NULL;
        struct Node* right = NULL;
    };
    

    在 C 中无效。您不能指定初始化程序。

    所以声明应该是这样的

    struct Node {
        int value;
        struct Node* left;
        struct Node* right;
    };
    

    函数insert_tree通过值获取指向根节点的指针。即函数处理的是原始指针的副本,所以在函数内更改原始指针的副本不会影响原始指针的值。

    您需要通过引用传递指针。在 C 中,通过引用传递意味着通过指针间接传递对象。考虑到内存分配可能会失败。希望该函数报告新节点的插入是否成功。

    另外,将第一个参数设置为指向根节点的指针,第二个参数设置为应该插入的值会更合适。

    无论如何这个函数声明

    void insert_tree(int value, Node* root) {
                                ^^^^^^^^^^
    

    是一个无效的 C 函数声明,因为与 C 中的 C++ 相反,您必须将关键字 struct 指定为

    void insert_tree(int value, struct Node* root) {
                                ^^^^^^^^^^^^^^^^^ 
    

    递归函数insert_tree可以这样定义

    int insert_tree( struct Node **root, int value ) 
    {
        if ( *root == NULL ) 
        {
            *root = (struct Node*) malloc( sizeof( struct Node ) );
            if ( *root != NULL )
            {
                ( *root )->value = value;
                ( *root )->left  = NULL;
                ( *root )->right = NULL;
            }
    
            return *root != NULL;
        }
        else 
        {
            if ( value < ( *root )->value ) 
            {
                return insert_tree( &( *root )->left, value );
            }
            else 
            {
                return insert_tree( &( *root )->right, value );
            }
        }
    }
    

    并且函数可以像这样调用

    int main( void ) 
    {
        struct Node *root = NULL;
        insert_tree( &root, 30 );
        printTree( root );
    }
    

    同样由于函数printTree 不会改变树,那么它的参数应该有限定符const

    void printTree( const struct Node* root ) {
        if (root != NULL) {
            printf("%i\n", root->value);
            printTree(root->left);
            printTree(root->right);
        }
    }
    

    这是一个演示 C 程序。

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    struct Node 
    {
        int value;
        struct Node *left;
        struct Node *right;
    };
    
    int insert_tree( struct Node **root, int value ) 
    {
        if ( *root == NULL ) 
        {
            *root = (struct Node*) malloc( sizeof( struct Node ) );
            if ( *root != NULL )
            {
                ( *root )->value = value;
                ( *root )->left  = NULL;
                ( *root )->right = NULL;
            }
    
            return *root != NULL;
        }
        else 
        {
            if ( value < ( *root )->value ) 
            {
                return insert_tree( &( *root )->left, value );
            }
            else 
            {
                return insert_tree( &( *root )->right, value );
            }
        }
    }
    
    void printTree( const struct Node* root ) {
        if (root != NULL) {
            printf("%i\n", root->value);
            printTree(root->left);
            printTree(root->right);
        }
    }
    
    int main(void) 
    {
        struct Node *root = NULL;
    
        const int N = 10;
    
        srand( ( unsigned int )time( NULL ) );
    
        for ( int i = 0; i < N; i++ )
        {
            insert_tree( &root, rand() % N );
        }
    
        printTree( root );
    
        return 0;
    }
    

    它的输出可能看起来像

    1
    0
    9
    4
    3
    7
    5
    6
    5
    8
    

    【讨论】:

      【解决方案2】:

      你应该通过引用传递root,改变这个:

      void insert_tree(int value, Node* root) {
          if (root == NULL) {
              root = (struct Node*) malloc(sizeof(Node));
              root->value = value;
              printf("%d\n", root->value);
              root->left = NULL;
              root->right = NULL;
          }
          else {
              if (root->value >= value) {
                  insert_tree(value, root->left);
              }
              else {
                  insert_tree(value, root->right);
              }
          }
      }
      

      到这里:

      void insert_tree(int value, Node** rootp) {
          struct Node * root = *rootp;
          if (root == NULL) {
              root = (struct Node*) malloc(sizeof(Node));
              root->value = value;
              printf("%d\n", root->value);
              root->left = NULL;
              root->right = NULL;
          }
          else {
              if (root->value >= value) {
                  insert_tree(value, &root->left);
              }
              else {
                  insert_tree(value, &root->right);
              }
          }
      }
      

      然后像这样调用插入:

      insert_tree(value , &root);
      

      这样可以确保对函数中的变量所做的任何更改都被保留。发生这种情况是因为当您改为按值传递时,该函数会创建您作为参数添加的变量的副本,因此对其进行的任何更改都不会影响原始值。

      编辑: 只是为了澄清,指针仍然是变量,并且作为变量它们持有一个值。 为了在函数中更改变量的值,您需要获取它的地址,指针的地址就像任何其他变量的地址一样通过&amp; 访问。如果 Root(您在 main 中声明的变量)不是指针,那么您也可以使用普通的 struct Node * root 作为参数,但既然不是,您应该坚持使用 struct Node ** root

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-09
        • 2022-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-05
        相关资源
        最近更新 更多