繁体   English   中英

在 C 中插入二叉搜索树

[英]Inserting into a binary search tree in C

我目前正在学习 C 以及一些数据结构,例如二叉搜索树等。我无法理解如何在 function 中准确更改指针值在某些情况下有效,而在其他情况下则不...我将附上我的一些代码我写。 这是一个插入 function 插入值在 BST 中的正确位置(它应该工作)。 我尝试使用指向指针的指针,以便能够使用 function 更改值。 即使它有效,我仍然很困惑它为什么会这样做。 我不太明白为什么我的插入 function 实际上会更改 BST,即使我在插入 function 中只使用局部变量(tmp,parent_ptr)并且我并没有真正取消引用除“tmp = *p2r”中的任何指针插入 function。

感谢您的帮助。

#include <stdio.h>
#include <stdlib.h>


struct TreeNode{
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

struct TreeNode** createTree(){
    struct TreeNode** p2r;
    p2r = malloc(sizeof(struct TreeNode*));
    *p2r = NULL;
    return p2r;
}

void insert(struct TreeNode** p2r, int val){
    // create TreeNode which we will insert
    struct TreeNode* new_node = malloc(sizeof(struct TreeNode));
    new_node -> val = val;
    new_node -> left = NULL;
    new_node -> right = NULL;
    //define onestep delayed pointer
    struct TreeNode* parent_ptr = NULL;
    struct TreeNode* tmp = NULL;
    tmp = *p2r;
    // find right place to insert node
    while (tmp != NULL){
        parent_ptr = tmp;
        if (tmp -> val < val) tmp = tmp->right;
        else tmp = tmp->left;
    }
    if (parent_ptr == NULL){
        *p2r = new_node;
    }
    else if (parent_ptr->val < val){ //then insert on the right
        parent_ptr -> right = new_node;
    }else{
        parent_ptr -> left = new_node;
    }
}

int main(){
    struct TreeNode **p2r = createTree();
    insert(p2r, 4);
    insert(p2r, 2);
    insert(p2r, 3);
    return 0;
}

虽然指针本身确实是局部变量,但它们指向 memory 中的特定位置。 当您使用 -> 符号取消引用指针时,您基本上是在访问 memory ,其中存储了指针指向的确切变量。 这就是为什么您的更改也反映在 function 之外的原因。

你基本上告诉了一个局部变量你的树存储在哪里,它有助于插入,然后它退出 scope。 树本身不是局部变量,因此更改会反映在其上。

我建议阅读指针的工作原理。

首先,永远记住关于指针的一件事,它们存储一个 memory 地址,而不是一个值。 例如:

int val = 5;
int copy = val;
int *original = &val;

printf("%d\n", val);
printf("%d\n", copy);
printf("%d\n", *original);

val = 8;

printf("%d\n", val);
printf("%d\n", copy);
printf("%d\n", *original);

在执行这段代码时,output 将是

5
5
5
8
5
8

注意,更改val的值时, copy的值保持不变,而 original 指向的值发生了变化。 发生这种情况是因为指针original指向 memory 位置val

现在,进入插入 function,虽然您只使用局部变量(tmp,parent_ptr),但请记住它们是指针变量,它们引用 memory 地址。 因此,无论何时在循环中,您遍历tmp -> righttmp -> left ,您实际上是在 memory 以正确的顺序从一个位置跳到另一个位置,这就是它起作用的原因。 下面的例子会更清楚。

     56 (A)
     /    \
    /      \
  45 (B)  60 (C)

考虑上面的 BST,括号中是 memory 地址。 让我们在这个 BST 中插入 40。 最初,tmp 将指向 A,地址为 56。现在 40 小于 56,因此 tmp 向左移动,现在指向 B,地址 45。再次向左移动,现在它是 null。 但是现在,parent_ptr 指向 B。因此 40 的新节点附加到 B 的左侧。

      56 (A)
     /    \
    /      \
  45 (B)  60 (C)
  /
 /
40 (D)

让我们逐步分析方法。

首先我们考虑以下简单的程序。

#include <stdio.h>
#include <stdlib.h>

struct TreeNode{
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

void create( struct TreeNode *head, int val )
{
    head = malloc( sizeof( struct TreeNode ) );
    
    head->val   = val;
    head->left  = NULL;
    head->right = NULL;
}

int main(void) 
{
    struct TreeNode *head = NULL;
    
    printf( "Before calling the function create head == NULL is %s\n",
            head == NULL ? "true" : "false" );
            
    create( head, 10 );
    
    printf( "After  calling the function create head == NULL is %s\n",
            head == NULL ? "true" : "false" );
            
    return 0;
}

程序 output 是

Before calling the function create head == NULL is true
After  calling the function create head == NULL is true

如您所见, main 中的指针head没有改变。 原因是 function 处理原始指针head值的副本。 因此更改副本不会影响原始指针。

如果将 function 参数重命名为head_parm (以区分原始指针名为head和 function 参数)那么您可以想象 ZC1C425268E68385D1AB5074C17A94F14 的定义和调用方式如下

create( head, 10 );

//...

void create( /*struct TreeNode *head_parm, int val */ )
{
    struct TreNode *head_parm = head;
    int val = 10;
    head_parm = malloc( sizeof( struct TreeNode ) );
    //...

也就是说,在 function 中创建了一个由参数 head 的值初始化的局部变量head_parm ,并且此 function 局部变量head_parm在 ZC1C425268E68385D1AB5074F 中更改。

这意味着 function arguments 是按值传递的。

要更改 main 中声明的原始指针head ,您需要通过引用传递它。

在 C 中,通过引用传递的机制是通过指向它的指针间接传递 object 来实现的。 因此,取消引用 function 中的指针,您将可以直接访问原始 object。

所以让我们用下面的方式重写上面的程序。

#include <stdio.h>
#include <stdlib.h>

struct TreeNode{
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

void create( struct TreeNode **head, int val )
{
    *head = malloc( sizeof( struct TreeNode ) );
    
    ( *head )->val   = val;
    ( *head )->left  = NULL;
    ( *head )->right = NULL;
}

int main(void) 
{
    struct TreeNode *head = NULL;
    
    printf( "Before calling the function create head == NULL is %s\n",
            head == NULL ? "true" : "false" );
            
    create( &head, 10 );
    
    printf( "After  calling the function create head == NULL is %s\n",
            head == NULL ? "true" : "false" );
            
    return 0;
}

现在程序 output 是

Before calling the function create head == NULL is true
After  calling the function create head == NULL is false            

在您的问题程序中,您没有像上面的程序那样声明指向头节点的指针

struct TreeNode *head = NULL;

您动态分配了这个指针。 实际上,您在程序中所做的是以下内容

#include <stdio.h>
#include <stdlib.h>

struct TreeNode{
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
};

void create( struct TreeNode **head, int val )
{
    *head = malloc( sizeof( struct TreeNode ) );
    
    ( *head )->val   = val;
    ( *head )->left  = NULL;
    ( *head )->right = NULL;
}

int main(void) 
{
    struct TreeNode **p2r = malloc( sizeof( struct TreeNode * ) );
    *p2r = NULL;
    
    printf( "Before calling the function create *p2r == NULL is %s\n",
            *p2r == NULL ? "true" : "false" );
            
    create( p2r, 10 );
    
    printf( "After  calling the function create *p2r == NULL is %s\n",
            *p2r == NULL ? "true" : "false" );
            
    return 0;
}

程序 output 是

Before calling the function create *p2r == NULL is true
After  calling the function create *p2r == NULL is false

与之前的程序相比,当您使用struct TreeNode **类型的表达式&head调用 function create时,您现在引入了一个中间变量p2r ,它存储表达式&head由于此代码片段的值

struct TreeNode **p2r = malloc( sizeof( struct TreeNode * ) );
*p2r = NULL;

那是你早早叫 function 创建像

create( &head, 10 );

现在实际上您正在调用 function 之类的

struct TreeNode **p2r = &head; // where head was allocated dynamically
create( p2r, 10 );

在您的程序中也会发生同样的情况。 那是在 function 插入取消引用指针 p2r 您可以直接访问指向头节点的指针

if (parent_ptr == NULL){
    *p2r = new_node;
    ^^^^ 
}

结果,function 将指针更改为通过指针p2r通过引用传递的头节点。

其他节点左右的数据成员也通过使用指针parent_ptr对它们的引用而改变

else if (parent_ptr->val < val){ //then insert on the right
    parent_ptr -> right = new_node;
    ^^^^^^^^^^^^^^^^^^^  
}else{
    parent_ptr -> left = new_node;
    ^^^^^^^^^^^^^^^^^^
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM