[英]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 -> right或tmp -> 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.