一、定义:
二叉排序树(二叉搜索树、二叉查找树)或者是空树,或者满足以下性质:`
(1)若它的左子树非空,则左子树上所有记录的值均小于根记录的值;
(2)若它的右子树非空,则右子树上所有记录的值均大于根记录的值;
(3)左、右子树本身又各是一颗二叉排序树。
——该定义源于《数据结构》李春葆
示例:

二、数据结构:
使用一个链表数据结构来表示,节点定义如下:
-
typedef struct STnode
-
{
-
int key;//数据信息
-
struct STnode *left;//指向左孩子
-
struct STnode *right;//指向右孩子
-
struct STnode *p;//指向父节点
-
} STnode;
三、插入操作:
二叉搜索树插入操作比较简单,将欲插入节点my_node从根节点开始比较,用cur_node表示当前被比较的节点,如果my_node.key > cur_node.key,则比较其右孩子,否则比较其左孩子,以此类推,直到找到叶节点为止,将my_node插入(大于所找到的叶节点则作为右孩子插入,小于则作为左孩子插入)。注意:插入操作一定是在叶节点上进的。
示例:插入关键字为9的节点,先和根节点比较(9>6),故与其右孩子节点比较(9>7),继续与其右孩子节点比较(9>8),由于该节点为叶节点,且9>8,则将节点作为右孩子插入。

代码:
-
//将节点my_node插入到二叉搜索树tree
-
STnode* STree_Insert(STnode *tree, STnode *my_node)
-
{
-
STnode *parent_node;//指向my_node的父节点
-
STnode *cur_node;//指向当前被比较的节点
-
-
//树为空
-
if(tree==NULL)
-
tree=my_node;
-
-
//树不为空
-
else
-
{
-
parent_node=NULL;
-
cur_node=tree;
-
while(cur_node!=NULL) //while循环寻找my_node的父节点
-
{
-
parent_node=cur_node;
-
if(my_node->key < cur_node->key)
-
cur_node=cur_node->left;
-
else
-
cur_node=cur_node->right;
-
}
-
my_node->p=parent_node;
-
if(my_node->key < parent_node->key)//插入到左子树
-
parent_node->left=my_node;
-
else //插入到右子树
-
parent_node->right=my_node;
-
}
-
return tree;
-
}
四、查找节点
思想比较简单,直接上代码:
-
//在tree里查找节点my_node
-
STnode *STree_Find(STnode *tree, int my_key)
-
{
-
STnode *cur_node=tree;
-
-
if(tree==NULL)
-
return NULL;
-
else
-
{
-
while(cur_node->key != my_key)
-
{
-
if(my_key < cur_node->key)
-
cur_node=cur_node->left;
-
else
-
cur_node=cur_node->right;
-
}
-
return cur_node;
-
}
-
}
五、返回最小关键字节点
根据二叉排序树的性质可知,关键字最小的节点一定是整棵树中最左边的节点,所以只需从根节点开始一直寻找left指
针,直到找到NULL为止。
代码:
-
//返回最小关键字所在节点
-
STnode *STree_Min(STnode *tree)
-
{
-
STnode *cur_node=tree;
-
-
while(cur_node->left)
-
{
-
cur_node=cur_node->left;
-
}
-
return cur_node;
-
}
六、返回最大关键字所在节点
根据二叉排序树的性质可知,关键字最大的节点一定是整棵树中最右边的节点,所以只需从根节点开始一直寻找right
指针,直到找到NULL为止。
代码:
-
//返回最大关键字所在节点
-
STnode *STree_Max(STnode *tree)
-
{
-
STnode *cur_node=tree;
-
-
while(cur_node->right)
-
{
-
cur_node=cur_node->right;
-
}
-
return cur_node;
-
}
七、查找节点后继(中序)
中序序列为:左子树、根、右子树,则一个节点若存在中序后继,则该后继必然位于该节点右边。
下图所示二叉排序树的中序为:2、3、4、6、7、13、15、17、18、20

——该图源于《算法导论》
如上所述,节点的后继一定位于节点右边,分两种情况:
1、该节点右孩子不为空:其后继必然是右子树上的最左节点(如节点6,后继为节点9);
2、该节点右孩子为NULL且该节点为其父节点的左孩子:后继为其父节点(如节点2,后继为节点3);
3、该节点右孩子为NULL且该节点为其父节点的右孩子:后继必为该节点所在“子树”根节点T的父节点,该根节点T
必为其父节点的左孩子,否则继续往上寻找(如节点13,满足该子树根节点为其父节点左孩子的子树根节点为6,
该节点为其父节点15的左孩子,故该节点后继为15)
代码:
-
STnode *STree_Successor(STnode *my_node)
-
{
-
STnode *cur_node;
-
STnode *successor;
-
if(my_node->right)//右孩子不为空
-
return STree_Min(my_node->right);
-
else //右孩子为空
-
{
-
if(my_node==my_node->p->left)//该节点为父节点左孩子
-
return my_node->p;
-
else//该节点为父节点右孩子
-
{
-
cur_node=my_node;
-
successor=cur_node->p;
-
while(successor!=NULL && cur_node!=successor->left)
-
{
-
cur_node=cur_node->p;
-
successor=cur_node->p;
-
}
-
return successor;
-
}
-
}
-
}
八、查找节点前驱(中序)
查找节点前驱的算法和查找节点后继的算法对称,也分为三种情况,分析分发一样,直接上代码。
代码:
-
STnode *STree_Predecessor(STnode *my_node)
-
{
-
STnode *cur_node;
-
STnode *predecessor;
-
if(my_node->left)//左孩子不为空
-
return STree_Max(my_node->left);
-
else //左孩子为空
-
{
-
if(my_node==my_node->p->right)//该节点为父节点右孩子
-
return my_node->p;
-
else//该节点为父节点左孩子
-
{
-
cur_node=my_node;
-
predecessor=cur_node->p;
-
while(predecessor!=NULL && cur_node!=predecessor->right)
-
{
-
cur_node=cur_node->p;
-
predecessor=cur_node->p;
-
}
-
return predecessor;
-
}
-
}
-
}
九、删除节点
一、节点删除:如下图所示

(1)删除叶节点(2、4、9、20):直接删除
(2)删除的节点只有一个孩子(左孩子或右孩子)(7、13):将这个节点的孩子提升替换该节点即可
如图所示,删除节点7:

(3)删除的节点有两个孩子节点(6、15、18)。需要保证删除该节点后仍然满足二叉排序树的性质,则必须要用该节点的中序后继替换该节点!再对该节点和替换节点的子树进行处理。
根据求节点中序后继的算法,一个节点的中序后继为该节点右子树上的最左节点,该后继节点必不存在左孩子,考虑两种情况,a、该后继节点为其右孩子(如节点6后继节点7为其右孩子);b、该后继节点不为其右孩子(如节点15,其后继节点17不为其右孩子)
情况a:直接将后继节点替换该节点,如下图删除节点6:

情况b:先用后继节点右孩子替换该后继节点,再用后继节点替换源节点,如下图删除节点15

综上所述,将删除节点my_node的情形总结为以下几种:
1、my_node为叶节点
2、my_node仅有一个孩子
2.1、my_node有仅左孩子
2.2、my_node仅有右孩子
3、my_node既有左孩子又有右孩子
3.1、my_node后继为my_node->rchild(my_node->rchild无左孩子)
3.2、my_node后继不为my_node->rchild(my_node->rchild有左孩子)
代码:(此处代码以个人理解的通俗方式书写,可能比较冗长,有较大优化空间)
-
STnode *STree_Delete(STnode *tree, STnode *my_node)
-
{
-
STnode *parent=my_node->p;
-
STnode *suc_parent;
-
STnode *successor;
-
-
if(my_node->left==NULL && my_node->right==NULL)//叶节点
-
{
-
if(parent==NULL)//只有一个节点
-
return NULL;
-
-
if(my_node==parent->left)
-
parent->left=NULL;
-
else
-
parent->right=NULL;
-
}
-
else if(my_node->left && my_node->right==NULL)//仅有左孩子
-
{
-
if(parent==NULL)//删除根节点
-
{
-
return tree->left;
-
}
-
-
if(my_node==parent->left)
-
{
-
parent->left=my_node->left;
-
my_node->left->p=parent;
-
}
-
else
-
{
-
parent->right=my_node->left;
-
my_node->left->p=parent;
-
}
-
}
-
else if(my_node->left==NULL && my_node->right)//仅有右孩子
-
{
-
if(parent==NULL)//删除根节点
-
{
-
return tree->right;
-
}
-
-
if(my_node==parent->left)
-
{
-
parent->left=my_node->right;
-
my_node->right->p=parent;
-
}
-
else
-
{
-
parent->right=my_node->right;
-
my_node->right->p=parent;
-
}
-
}
-
else//有两个孩子
-
{
-
successor=STree_Successor(my_node);
-
-
if(successor == my_node->right)//my_node后继为my_node->rchild
-
{
-
if(my_node==parent->left)
-
{
-
parent->left=successor;
-
successor->p=parent;
-
successor->left=my_node->left;
-
}
-
else
-
{
-
parent->right=successor;
-
successor->p=parent;
-
successor->left=my_node->left;
-
}
-
}
-
else //my_node后继不为my_node->rchild
-
{
-
suc_parent=successor->p;
-
suc_parent->left=successor->right;
-
successor->right->p=suc_parent;
-
-
successor->right=my_node->right;
-
successor->left=my_node->left;
-
successor->p=my_node->p;
-
}
-
}
-
if(parent==NULL)//删除根节点
-
return successor;
-
else
-
return tree;
-
}
二、主函数,测试用例及其头文件等
-
#include <stdio.h>
-
#include <stdlib.h>
-
typedef struct STnode
-
{
-
int key;//数据信息
-
struct STnode *left;//指向左孩子
-
struct STnode *right;//指向右孩子
-
struct STnode *p;//指向父节点
-
} STnode;
-
STnode* STree_Insert(STnode *tree, STnode *my_node);
-
void STree_Inorder(STnode *tree);
-
STnode *STree_Creat(STnode *tree, int arr[], int n);
-
STnode *STree_Find(STnode *tree, int my_key);
-
STnode *STree_Max(STnode *tree);
-
STnode *STree_Min(STnode *tree);
-
STnode *STree_Successor(STnode *my_node);
-
STnode *STree_Predecessor(STnode *my_node);
-
STnode *STree_Delete(STnode *tree, STnode *my_node);
-
-
int main(void)
-
{
-
int i;
-
int arr[]={15,6,20,3,7,17,21,4,13,19,9};
-
STnode *tree=NULL;
-
STnode *p;
-
-
tree=STree_Creat(tree, arr, 11);
-
-
STree_Inorder(tree);
-
printf("\n");
-
-
p=STree_Find(tree, 7);
-
printf("find the key: %d\n",p->key);
-
-
p=STree_Max(tree);
-
printf("the max key: %d\n", p->key);
-
-
p=STree_Min(tree);
-
printf("the min key: %d\n",p->key);
-
-
p=STree_Successor(STree_Find(tree, 7));
-
printf("the successor of 7 is %d\n",p->key);
-
-
p=STree_Predecessor(STree_Find(tree, 7));
-
printf("the predessor of 7 is %d\n",p->key);
-
-
//以下三组删除操作在同一颗树上连续进行;
-
p=STree_Delete(tree, STree_Find(tree, 4));
-
printf("delete the leaf node 4:");
-
STree_Inorder(p);
-
printf("\n");
-
-
p=STree_Delete(tree, STree_Find(tree, 20));
-
printf("delete the node 20 which only have left child:");
-
STree_Inorder(p);
-
printf("\n");
-
-
p=STree_Delete(tree, STree_Find(tree, 7));
-
printf("delete the node 7 which only have right child:");
-
STree_Inorder(p);
-
printf("\n");
-
-
//重置二叉排序树,实验删除节点6和15
-
tree=NULL;
-
tree=STree_Creat(tree, arr, 11);
-
-
p=STree_Delete(tree, STree_Find(tree, 6));
-
printf("delete the node 6 which have two children:");
-
STree_Inorder(p);
-
printf("\n");
-
-
p=STree_Delete(tree, STree_Find(tree, 15));
-
printf("delete the node 15 which have two children:");
-
STree_Inorder(p);
-
printf("\n");
-
-
return 0;
-
}