前言
本文主要总结一下如何在链式二叉树和数组二叉树中删除一个值。
源代码
- 在链式二叉树中删除一个值
l_t_del.c
/*
- Delete a node from a linked binary search tree
*/
void delete(TREE_TYPE value)
{
TreeNode *current;
TreeNode **link;
/*
* First, locate the value. It must exist in the tree or this routine
* will abort the program.
*/
link = &tree;
while ((current = *link) != NULL && value != current->value){
if (value < current->value)
link = ¤t->left;
else
link = ¤t->right;
}
assert(current != NULL);
/*
* We've found the value. See how many children it has.
*/
if (current->left == NULL && current->right == NULL){
/*
* It is a leaf; no children to worry about!
*/
*link = NULL;
free(current);
}
else if (current->left == NULL || current->right == NULL){
/*
* The node has only one child, so the parent will simply
* inherit it.
*/
if (current->left != NULL)
*link = current->left;
else
*link = current->right;
free(current);
}
else {
/*
* The node has two children! Replace its value with the
* largest value from its left subtree, and then delete that
* node instead.
*/
TreeNode *this_child;
TreeNode *next_child;
this_child = current->left;
next_child = this_child->right;
while (next_child != NULL){
this_child = next_child;
next_child = this_child->right;
}
/*
* Delete the child and replace the current value with
* this_child's value.
*/
value = this_child->value;
delete(value);
current->value = value;
}
}
主要分为4种情况进行处理:
- 要删除的值未在树中找到,终止程序;
- 找到要删除的值的位置,左右子节点为空。把指向该地址的指针变量设为NULL并释放内存空间。
- 找到要删除的值的位置,左右子节点有一个非空。把指向该地址的指针变量设为它左或右子节点指向的地址,并释放原先指向的内存空间。
- 找到要删除的值的位置,左右子节点都不为空。从它的左子节点中寻找最大的值,用该值替换要删除的值,并把存放最大值的内存空间释放。此时,原先要删除的值的位置存放的是它的左子节点中的最大值,而原先存放这个最大值的内存空间已被释放。
最后一种情况存放那个最大值的结构体中,它的left指针可能指向NULL,也可能指向一个存放数据的地址。而它的right指针始终指向NULL,因为最大的值已被找到。
- 在静态数组形式的二叉搜索树中删除一个值
a_t_del.c
/*
* Delete a node from an arrayed binary search tree
*/
void delete(TREE_TYPE value)
{
int current;
int left;
int right;
int left_subtree_empty;
int right_subtree_empty;
/*
* First, locate the value. It must exist in the tree or this routine
* will abort the program.
*/
current = 1;
while (tree[current] != value){
if (value < tree[current])
current = left_child(current);
else
current = right_child(current);
assert(current < ARRAY_SIZE);
assert(tree[current] != 0);
}
/*
* We've found the value. If is is a leaf, simply set it to zero.
* Otherwise, if its left subtree is not empty, replace the node's value
* with the rightmost (largest) child from its left subtree, and then
* delete that node. Otherwise, replace the value with the leftmost
* (smallest) child from its right subtree, and delete that node.
*/
left = left_child(current);
right = right_child(current);
left_subtree_empty = left > ARRAY_SIZE || tree[left] == 0;
right_subtree_empty = right > ARRAY_SIZE || tree[right] == 0;
if (left_subtree_empty && right_subtree_empty)
/*
* The value has no children; simply set it to zero.
*/
tree[current] = 0;
else {
int this_child;
int next_child;
if (!left_subtree_empty){
/*
* The left subtree is nonempty. Find its rightmost
* child.
*/
this_child = left;
next_child = right_child(this_child);
while (next_child < ARRAY_SIZE
&& tree[next_child] != 0){
this_child = next_child;
next_child = right_child(this_child);
}
}
else {
/*
* The right subtree is nonempty. Find its leftmost
* child.
*/
this_child = right;
next_child = left_child(this_child);
while (next_child < ARRAY_SIZE
&& tree[next_child] != 0){
this_child = next_child;
next_child = left_child(this_child);
}
}
/*
* Delete the child and replace the current value with
* this_child's value.
*/
value = tree[this_child];
delete(value);
tree[current] = value;
}
}
主要分为3种情况进行处理:
- 未在树中找到要删除的值,终止程序。
- 在树中找到要删除的值的位置,没有左右子节点。把该位置设为0。
- 在树中找到要删除的值的位置,有左右子节点。如果有左子节点则优先在左子节点中寻找最大值,并把该值复制到要删除的值的位置;如果没有左子节点则在右子节点中寻找最小值,并把该值复制到要删除的值的位置。
总结
这两种方式始终都是以二叉树的排序进行删除操作的,左子节点的值永远比父节点的值小,右子节点的值永远比父节点的值大。
在链式的删除中,可以随时释放节点的内存空间,需要插入时再分配。而数组则不行,始终占据着一块内存空间。
当要删除的节点有子节点时,数组需要进行的递归操作比较多,特别是当该子节点还有多个子节点时,链式需要进行的递归操作比较少,更多的是改变指针指向的地址即可。链式比数组在内存利用率和删除操作上效率更高。