[英]Binary Search Tree node insertion not working properly
我有一个作业分配来实现一个二进制搜索树(创建,删除,搜索)。 我使用了老师提供的示例,但无法使其正常工作。
到目前为止,这是我的代码:
void insert_node(int k){
struct node *nodnou,*flow,*parent;
nodnou = (struct node*)malloc(sizeof(node));
nodnou->st = NULL;
nodnou->dr = NULL;
nodnou->nr = k;
if (root==NULL)
{
root = (struct node*)malloc(sizeof(node));
root = nodnou;
}
else
{
flow = (struct node*)malloc(sizeof(node));
parent = (struct node*)malloc(sizeof(node));
flow = root;
parent = root;
while (((flow->st!=NULL) || (flow->dr!=NULL)) && flow!=NULL)
{
if (k<flow->nr)
{
parent = flow;
flow = flow->st;
}
else
{
parent = flow;
flow = flow->dr;
}
}
if (k<flow->nr)
{
parent->st = nodnou;
}
else
{
parent->dr = nodnou;
}
}
}
思维方式:此函数获取我们要插入的节点的值作为k参数。 该函数只会插入树的根(根是全局变量)。
我认为我最大的问题是while
循环遍历树以查找新节点的位置。
如果我使用while (flow!=NULL)
,它将不起作用,因为流指针获得了对不存在对象的赋值。 请帮助我了解我错了(作业)。
您的代码有几个重要的缺陷,其中最重要的一个就是对动态内存分配在C中的工作方式的误解。 永远不要遵循这样的模式:
Type *pointer = malloc(sizeof(Type));
pointer = <<something else>>
从字面上看,它会泄漏内存,并且在短短两行之内就没有任何帮助 。 这不是像Java或C#这样的基于对象引用的语言。 指针是保存内存地址的变量。 就像int
可以容纳整数一样,指针也可以容纳address 。 就像下面的例子一样:
int n = 6;
n = 5; //Hmm. Where did the 6 go? Oh yeah, We overwrote it with 5.
您将失去使用指针执行相同操作的分配链接:
struct node *root = malloc(sizeof(*root));
root = nodnou; // memory allocated above is gone. forever leaked.
指针是变量 。 就像任何其他变量一样,它们保存值。 但是,在使用指针的情况下,其值为address 。 您可以在C语言中拥有几乎所有内容的指针,包括指向指针的指针。 保存指针变量地址的变量。 我之所以提出这些建议,是因为它们为您的插入要求提供了特别优雅的解决方案。
以下是二进制树插入的一般实现,该插入不支持树中的重复项(如果允许重复项,则代码会更短)。 此外,它使用提供的函数参数之外的零个局部变量来执行此操作,因此我挑战您剖析并确定其工作方式。 它甚至可以在最初为NULL的树根指针上工作,从而无需使用特殊的大小写if (root) {} else {}
逻辑:
void insert_node(struct node **pp, int k)
{
while (*pp)
{
if (k < (*pp)->nr) // move down left side?
pp = &(*pp)->st;
else if ((*pp)->nr < k) // move down right side?
pp = &(*pp)->dr;
else return; // found the key, no dupes. leave
}
// pp contains the address of the pointer we need to set.
*pp = malloc(sizeof(**pp));
(*pp)->st = (*pp)->dr = NULL;
(*pp)->nr = k;
}
如果您的树应支持重复项,则需要对其插入的哪一侧保持一致,但会大大缩短上述算法:
void insert_node(struct node **pp, int k)
{
while (*pp)
pp = (k < (*pp)->nr) ? &(*pp)->st : &(*pp)->dr;
// pp contains the address of the pointer we need to set.
*pp = malloc(sizeof(**pp));
(*pp)->st = (*pp)->dr = NULL;
(*pp)->nr = k;
}
无论哪种情况,都可以在调用方调用,如下所示:
struct node *root = NULL;
insert(&root, 5);
insert(&root, 10);
insert(&root, 7);
...etc...
我认为您应该使用while(flow!= NULL)并在此之后将元素作为流插入。 现在的方式是在不应该停止的情况下停止运行,并在停止时进行奇怪的操作。 尝试使用笔和纸研究一些示例。
差不多了 赶上!
首先,您需要了解更好的内存分配。 实际上,您只需要在函数中进行第一个malloc()
调用。 那就是您在每次insert_node()
调用期间为要附加到树上的节点分配的内存。 您正在执行的所有剩余malloc
都是不必要的。 似乎您从直觉上感觉需要为正在使用的其他指针分配内存,但是它们全部都是临时的,不需要任何分配,只需在尝试取消引用它们之前就分配给有效节点。 实际上,这些不必要的分配会在如下代码中创建所谓的内存泄漏(您请求的内存而无法释放):
root = (struct node*)malloc(sizeof(node));
root = nodnou;
第二个Assignmet( root = nodnou
)覆盖前一个malloc()
调用的结果,由于您没有将覆盖的指针值保存在其他任何地方,因此您将无法再释放该内存,它将被标记为用于您的应用程序的生命周期!
接下来,您可以简化遍历树寻找插入点的代码。 您似乎担心流变为NULL,但这无关紧要。 重要节点是父节点。 while循环结束后,它将指向需要链接插入的节点的实际节点。 这是您代码的修改版本。
void insert_node(int k) {
struct node *nodnou, *flow, *parent;
// this is the only memory allocation that should be done
nodnou = (struct node*)malloc(sizeof(node));
nodnou->st = NULL;
nodnou->dr = NULL;
nodnou->nr = k;
parent = NULL;
if( root == NULL ) {
root = nodnou;
} else {
flow = root;
// We will walk the tree in order until we reach the bottom of the
// tree (flow becomes null). What we are trying to do is to find out
// the node that will become the parent of the new node we are inserting
// It doesn't matter if flow becomes NULL, the important value after the
// while loop ends is parent
while( flow != NULL ) {
// update the current potential parent node
parent = flow;
if( k < flow->nr ) {
// the number we are inserting is lower than the flow node, walk to the left
flow = flow->st;
} else {
// the number we are inserting is greater or equal than the flow node,
// walk to the right
flow = flow->dr;
}
}
// We have reached the bottom, now compare number again with the node that
// will become parent, to find out if we need to link this node to the left
// or to the right of the parent node
if( k < parent->nr ) {
parent->st = nodnou;
} else {
parent->dr = nodnou;
}
}
}
而已。 尝试对其余的树操作进行编码,不要犹豫,请问是否感到困惑。 =)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.