一、定义:
二叉排序树(二叉搜索树、二叉查找树)或者是空树,或者满足以下性质:`
(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,则将节点作为右孩子插入。
代码:
-
- STnode* STree_Insert(STnode *tree, STnode *my_node)
- {
- STnode *parent_node;
- STnode *cur_node;
-
-
- if(tree==NULL)
- tree=my_node;
-
-
- else
- {
- parent_node=NULL;
- cur_node=tree;
- while(cur_node!=NULL)
- {
- 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;
- }
四、查找节点
思想比较简单,直接上代码:
-
- 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)
- {
- 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
- {
- 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");
-
-
- 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;
- }