二叉查找树重要性质:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
现有,如下一棵二叉查找树。
(图1)
现在,若要删除图1中,任意节点,需要考虑如下三种情况:
(1)需要删除的节点下并没有其他子节点。
(2)需要删除的节点下有一个子节点(左或右)。
(3)需要删除的节点下有两个子节点(既左右节点都存在)。
第一种情况直接删除即可,下面,直接讨论第二种情况。
若我们要删除的是3号节点,由图1可以看到,它下面还有一个4号子节点。由下图2,可以看出,对于这种办法,我们只需要想办法,让5号节点的左子树的指针指向4就可以了。
(图2)
第三种情况,既我们要删除的节点下,有2个子节点。如图3,第一种方法:我们先在需要删除的节点的右子树中,找到一个最小的值(因为右子树中的节点的值一定大于根节点)。然后,用找到的最小的值与需要删除的节点的值替换。然后,再将最小值的原节点进行删除(图4)。
第二中方法:在找需要删除的节点左子树中,找到一个最大的值,其余操作和第一种方法相同。
(图3)
(图4)
好了,思路大概就这样了。下面见代码。
/*======================================*/
/*二叉查找树的查找返回查找目标的父节点 */
/*======================================*/
treenode *tree_find_parent(treenode *ptr, int data, int *pos)
{
treenode *previous = ptr;
*pos = 0;
while(ptr != NULL)
{
if(ptr->data == data)
return previous;
else
{
previous = ptr; //记录父节点指针
if(ptr->data > data) //输入数据小于当前节点值
{
*pos = -1; //表示目标节点是父节点的左子节点
ptr = ptr->left; //转向左子节点
}
else //输入数据大于当前节点值
{
*pos = 1; //表示目标节点是父节点的右子节点
ptr = ptr->right; //转向右子节点
}
}
}
return NULL; //未找到目标
}
/*==============================*/
/*二叉查找树的删除 */
/*==============================*/
treenode *tree_node_del(treenode *root, int data)
{
treenode *parent, *dest;
int pos; //记录是目标节点是父节点的左/右子节点
//先遍历二叉树找到要删除的节点
parent = tree_find_parent(root, data, &pos);
if(NULL == parent) //没有找到要删除的节点
{
printf("not found !\n"); //打印为找到
return root;
}
switch(pos) //设定要删除的节点
{
case -1: dest = parent->left;
break;
case 1: dest = parent->right;
break;
default: dest = parent;
}
if(NULL == dest->left) //要删除的节点没有左树
{
switch(pos)
{
case -1: parent->left = dest->right;
break;
case 1: parent->right = dest->right;
break;
default: root = root->right;
}
free(dest);
}
else if(NULL == dest->right) //要删除的节点没有右树
{
switch(pos)
{
case -1: parent->left = dest->left;
break;
case 1: parent->right = dest->left;
break;
default: root = root->left;
}
free(dest);
}
else //要删除的节点左/右树都有
{
//1、首先找到要删除节点的右树中最小的节点和它的父节点
treenode *temp = dest->right; //temp存放删除节点的右树中最小的节点
treenode *pre = dest; //pre存放temp的父节点
while(temp->left != NULL)
{
pre = temp;
temp = temp->left;
}
//2、把要删除的节点替换成要删除节点的右树中最小的节点
dest->data = temp->data;
//3、删掉temp
pre->left = temp->right; //此时temp不可能有左树,否则他不是最小的
free(temp);
}
return root;
}