[英]Deleting the leaf nodes of a binary search tree - segmentation fault
我试图删除我以非递归方式创建的BST中的叶节点。 问题是,当我尝试删除叶节点时,我遇到了分段错误,却不知道为什么会发生这种情况。 我相信我已评论的代码导致了问题。 删除该节点的想法是错误的还是从BST删除任何节点的其他方法?
#include <stdio.h>
#include <stdlib.h>
struct BSTnode
{
int data;
struct BSTnode *left,*right,*parent;
};
typedef struct BSTnode node;
void insert(node **root,int val)
{
node *ptr,*newptr;
newptr=(node *)malloc(sizeof(node));
newptr->left=NULL;
newptr->right=NULL;
newptr->parent=NULL;
newptr->data=val;
if((*root)==NULL)
{
(*root)=newptr;
return;
}
ptr=(*root);
while(ptr!=NULL && ptr->data!=val)
{
if(ptr->data >val )
{
if(ptr->left!=NULL)
ptr=ptr->left;
else
{
ptr->left=newptr;
newptr->parent=ptr;
break;
}
}
else
{
if(ptr->right!=NULL)
ptr=ptr->right;
else
{
ptr->right=newptr;
newptr->parent=ptr;
break;
}
}
}
}
void deleteLeaf(node **root)
{
node *leafParent=NULL;
if((*root)->left!=NULL)
deleteLeaf(&((*root)->left));
if((*root)->right!=NULL)
deleteLeaf(&((*root)->right));
if((*root)->left==NULL && (*root)->right==NULL)
{
/* leafParent=(*root)->parent;
if(leafParent->left==(*root))
leafParent->left=NULL;
else
leafParent->right=NULL;
*/
free(*root);
}
}
void inorder(node *root)
{
if(root->left!=NULL)
inorder(root->left);
printf(" %d ", root->data);
if(root->right!=NULL)
inorder(root->right);
}
main()
{
node *root=NULL;
int i,n,val;
printf("\n How many elements ?");
scanf("%d",&n);
for(i=0;i<n;++i)
{
scanf("%d",&val);
insert(&root,val);
}
printf("\n Inorder traversal : ");
inorder(root);
deleteLeaf(&root);
printf("\n Inorder traversal : ");
inorder(root);
}
确实,在deleteLeaf函数中存在某些情况,其中(注释掉的)leafParent为NULL,然后一旦使用leafParent-> left对其取消引用,就会出现段错误。 因此,您需要按照以下步骤进行检查:
leafParent=(*root)->parent;
if (leafParent)
{
if(leafParent->left==(*root))
{
leafParent->left=NULL;
}
else
{
leafParent->right=NULL;
}
}
这将防止seg错误,但是我不清楚该函数是否最终会完成您想要的工作...换句话说,您的逻辑可能仍需要进行一些调整才能使其仅删除叶节点(如果那样)是您要尝试执行的操作)。
我相信您并不是真正在删除叶子-您正在递归删除整个树:对于每个节点,首先删除叶子,然后检查它是否是叶子((left == NULL) && (right == NULL))
-显然是因为您刚刚删除了它的后代-然后将其删除。
正如克里斯很快指出的那样,第一个问题是您没有检查leafParent是否可以变为NULL。 请检查该节点,或者为根节点设置父节点自身,以免取消引用NULL。 另一个(恕我直言更糟)的问题是,一旦释放内存,您就不会使指针无效-一个好的习惯是,一旦释放它们,便将指针设置为NULL。 如果以后不进行检查,则可能会发生段错误,但以后不会在其他部分可能分配内存块时使用指针来无提示地破坏内存。 您甚至可以编写一个包装器来为您做这件事(它也可以检查是否有尝试释放NULL指针的尝试,这实际上是一个有效的操作,只是默默地无所作为-至少在某些平台上如此)。 在这种特殊情况下,一旦删除了整个树(实际上就是这样),您仍然可以在main()内部找到指向已删除根节点的指针,然后立即使用它。
作为挑剔的旁注,在代码中,它要么是int main(void) { ... }
要么是int main(int argc, int **argv) { ... }
而不仅仅是main()
。 :-)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.