假设我们有以下BST:
5 (level 0)
3 8 (level 1)
2 4 7 9 (level 2)
当尝试删除级别0或1时,一切正常,但是当删除级别2.我得到分段错误
[1] 18636 segmentation fault ./a.out
这是我的代码:
#include<stdio.h>
#include<stdlib.h>
struct node
{
int key;
struct node *left, *right;
};
// A function to create a new BST node
struct node *newNode(int item)
{
struct node *temp = (struct node *)malloc(sizeof(struct node));
temp->key = item;
temp->left = temp->right = NULL;
return temp;
}
// A function to insert a new node with given key in BST
struct node* insert(struct node* node, int key)
{
// If the tree is empty, return a new node
if (node == NULL) return newNode(key);
// Otherwise, recur down the tree
if (key < node->key)
node->left = insert(node->left, key);
else if (key > node->key)
node->right = insert(node->right, key);
// return the (unchanged) node pointer
return node;
}
// Given a non-empty binary search tree, return the node with minimum
// key value found in that tree. Note that the entire tree does not
// need to be searched.
struct node * minValueNode(struct node* node)
{
struct node* current = node;
// loop down to find the leftmost leaf
while (current->left != NULL)
current = current->left;
return current;
}
struct node* deleteNode(struct node* root, int key)
{
// base case
if (root == NULL) return root;
// If the key to be deleted is smaller than the root's key,
// then it lies in left subtree
if (key < root->key)
root->left = deleteNode(root->left, key);
// If the key to be deleted is greater than the root's key,
// then it lies in right subtree
else if (key > root->key)
root->right = deleteNode(root->right, key);
// if key is same as root's key, then This is the node
// to be deleted
else
{
// node with only one child or no child
if (root->left == NULL)
{
struct node *temp = root->right;
free(root);
return temp;
}
else if (root->right == NULL)
{
struct node *temp = root->left;
free(root);
return temp;
}
// node with two children: Get the inorder successor (smallest
// in the right subtree)
struct node* temp = minValueNode(root->right);
// Copy the inorder successor's content to this node
root->key = temp->key;
// Delete the inorder successor
root->right = deleteNode(root->right, temp->key);
}
return root;
}
void deleteGivenLevel(struct node* root, int level)
{
if (root == NULL)
{
return;
}
if (level == 0)
{
printf("\n %d will be removed!", root->key);
root = deleteNode(root, root->key);
}
else if (level > 0)
{
deleteGivenLevel(root->left, level-1);
deleteGivenLevel(root->right, level-1);
}
}
// A function to do inorder traversal of BST
void inorder(struct node *root)
{
if (root != NULL)
{
inorder(root->left);
printf("\t %d", root->key);
inorder(root->right);
}
}
int main()
{
int input, level;
struct node *root = NULL;
printf("\nEnter a number to go into the BST: ");
scanf("%d",&input);
root = insert(root, input);
while(1)
{
printf("\nEnter another number (0 to stop): ");
scanf("%d",&input);
if (input==0)
{
break;
}
insert(root, input);
}
// print inoder traversal of the BST
printf("\nBefore Deletion:");
inorder(root);
printf("\nWhich level would you like to delete? ");
scanf("%d",&level);
deleteGivenLevel(root, level);
// print inoder traversal of the BST
printf("\nAfter Deletion:");
inorder(root);
return 0;
}
我还注意到它只在我尝试删除级别0或1时才有效。除此之外它不起作用。如果我添加更多节点,我尝试删除。在这种情况下,它根本不会删除任何内容。
答案 0 :(得分:0)
问题是deleteNode
。
当您使用leaf(deleteNode
和left
个孩子的节点呼叫right
时
是NULL
)因为它的参数,这个块被执行:
if (root->left == NULL)
{
struct node *temp = root->right;
free(root);
return temp;
}
else if (root->right == NULL)
{
struct node *temp = root->left;
free(root);
return temp;
}
这里的问题是你释放了叶子,但是父亲是叶子 指向叶子没有得到更新,因此它一直指向 释放内存,当你再次遍历树时,你访问无效 记忆。这会产生未定义的行为,并以段错误结束。
如果您逐步完成deleteGivenLevel
功能,最终
root
将是key == 3
的节点。然后你做
deleteGivenLevel(root->left, level-1);
其中root->left
是key == 2
且level-1
为0的叶子。这意味着
在下次通话中
if (level == 0)
{
printf("\n %d will be removed!", root->key);
root = deleteNode(root, root->key);
}
执行,root
是key == 2
的叶子。这是问题所在,
该节点已被删除,但因为它是一个离开,所以key == 3
的节点为
left
元素指向叶子,不会更新。
快速解决方法是将父节点传递给deleteNode
和
deleteGivenLevel
以便您可以正确更新父节点。但是你
应该重新考虑你删除策略。
struct node* deleteNode(struct node* root, int key, struct node *parent)
{
// base case
if (root == NULL) return root;
// If the key to be deleted is smaller than the root's key,
// then it lies in left subtree
if (key < root->key)
root->left = deleteNode(root->left, key, NULL); // you could also call
// with root as the
// 3rd argument, but
// you update the parent in this line
// anyway
// If the key to be deleted is greater than the root's key,
// then it lies in right subtree
else if (key > root->key)
root->right = deleteNode(root->right, key, NULL); // same as above
// if key is same as root's key, then This is the node
// to be deleted
else
{
// node with only one child or no child
if (root->left == NULL)
{
struct node *temp = root->right;
// update only if parent is present
if(parent)
{
if(root->key < parent->key)
parent->left = temp;
else
parent->right = temp;
}
free(root);
return temp;
}
else if (root->right == NULL)
{
struct node *temp = root->left;
// update only if parent is present
if(parent)
{
if(root->key < parent->key)
parent->left = temp;
else
parent->right = temp;
}
free(root);
return temp;
}
// node with two children: Get the inorder successor (smallest
// in the right subtree)
struct node* temp = minValueNode(root->right);
// Copy the inorder successor's content to this node
root->key = temp->key;
// Delete the inorder successor
root->right = deleteNode(root->right, temp->key, root);
}
return root;
}
void deleteGivenLevel(struct node* root, int level, struct node *parent)
{
if (root == NULL)
{
return;
}
if (level == 0)
{
printf("\n %d will be removed!", root->key);
root = deleteNode(root, root->key, parent);
}
else if (level > 0)
{
deleteGivenLevel(root->left, level-1, root);
deleteGivenLevel(root->right, level-1, root);
}
}
void free_tree(struct node *root)
{
if(root == NULL)
return;
free_tree(root->left);
free_tree(root->right);
free(root);
}
现在,当您使用真实根调用deleteGivenLevel
时,应设置parent
到NULL
。
我使用此main
函数检查了代码,请注意deleteGivenLevel
是如何进行的
使用parent == NULL
调用:
int main()
{
int level;
struct node *root = NULL;
int vals[] = { 5, 3, 8, 2, 4, 7, 9 };
for(size_t i = 0; i < sizeof vals / sizeof *vals; ++i)
root = insert(root, vals[i]);
// print inoder traversal of the BST
printf("\nBefore Deletion:");
inorder(root);
puts("");
level = 2;
deleteGivenLevel(root, level, NULL);
// print inoder traversal of the BST
printf("\nAfter Deletion:");
inorder(root);
puts("");
free_tree(root);
return 0;
}
这是输出:
$ valgrind ./a
==24038== Memcheck, a memory error detector
==24038== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==24038== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==24038== Command: ./a
==24038==
Before Deletion: 2 3 4 5 7 8 9
2 will be removed!
4 will be removed!
7 will be removed!
9 will be removed!
After Deletion: 3 5 8
==24038==
==24038== HEAP SUMMARY:
==24038== in use at exit: 0 bytes in 0 blocks
==24038== total heap usage: 8 allocs, 8 frees, 1,192 bytes allocated
==24038==
==24038== All heap blocks were freed -- no leaks are possible
==24038==
==24038== For counts of detected and suppressed errors, rerun with: -v
==24038== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
我还检查了更多节点的代码并删除了不同的级别,我没有 得到一个单一的段错误,无论如何它似乎打印出正确的结果。