一、定义:
二叉排序树(二叉搜索树、二叉查找树)或者是空树,或者满足以下性质:`
(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;
}
}
}
七、删除节点
删除节点操作是二叉排序树所有操作中最复杂最难理解的操作,在下一篇博文
(链接地址http://blog.csdn.net/xiaowang627/article/details/51336272 )中对其进行详细的图文描述,
同时对所有函数进行测试。