二叉排序树的实现

一、定义:

  二叉排序树(二叉搜索树、二叉查找树)或者是空树,或者满足以下性质:`

(1)若它的左子树非空,则左子树上所有记录的值均小于根记录的值;

(2)若它的右子树非空,则右子树上所有记录的值均大于根记录的值;

(3)左、右子树本身又各是一颗二叉排序树。

    ——该定义源于《数据结构》李春葆

示例:

                 

二、数据结构:

使用一个链表数据结构来表示,节点定义如下:

[objc]  view plain  copy
  1. typedef struct STnode  
  2. {  
  3.     int key;//数据信息  
  4.     struct STnode *left;//指向左孩子  
  5.     struct STnode *right;//指向右孩子  
  6.     struct STnode *p;//指向父节点  
  7. } STnode;  

三、插入操作:

二叉搜索树插入操作比较简单,将欲插入节点my_node从根节点开始比较,用cur_node表示当前被比较的节点,如果my_node.key > cur_node.key,则比较其右孩子,否则比较其左孩子,以此类推,直到找到叶节点为止,将my_node插入(大于所找到的叶节点则作为右孩子插入,小于则作为左孩子插入)。注意:插入操作一定是在叶节点上进的


示例:插入关键字为9的节点,先和根节点比较(9>6),故与其右孩子节点比较(9>7),继续与其右孩子节点比较(9>8),由于该节点为叶节点,且9>8,则将节点作为右孩子插入。


代码:

[objc]  view plain  copy
  1. //将节点my_node插入到二叉搜索树tree  
  2. STnode* STree_Insert(STnode *tree, STnode *my_node)  
  3. {  
  4.     STnode *parent_node;//指向my_node的父节点  
  5.     STnode *cur_node;//指向当前被比较的节点  
  6.       
  7.     //树为空  
  8.     if(tree==NULL)  
  9.         tree=my_node;  
  10.           
  11.     //树不为空      
  12.     else  
  13.     {  
  14.         parent_node=NULL;  
  15.         cur_node=tree;  
  16.         while(cur_node!=NULL//while循环寻找my_node的父节点  
  17.         {  
  18.             parent_node=cur_node;  
  19.             if(my_node->key < cur_node->key)  
  20.                 cur_node=cur_node->left;  
  21.             else  
  22.                 cur_node=cur_node->right;  
  23.         }  
  24.         my_node->p=parent_node;  
  25.         if(my_node->key < parent_node->key)//插入到左子树  
  26.             parent_node->left=my_node;  
  27.         else                               //插入到右子树    
  28.             parent_node->right=my_node;  
  29.     }  
  30.     return tree;  
  31. }  


四、查找节点

思想比较简单,直接上代码:

[objc]  view plain  copy
  1. //在tree里查找节点my_node  
  2. STnode *STree_Find(STnode *tree, int my_key)  
  3. {  
  4.     STnode *cur_node=tree;  
  5.       
  6.     if(tree==NULL)  
  7.         return NULL;  
  8.     else  
  9.     {  
  10.         while(cur_node->key != my_key)  
  11.         {  
  12.             if(my_key < cur_node->key)  
  13.                 cur_node=cur_node->left;  
  14.             else  
  15.                 cur_node=cur_node->right;  
  16.         }  
  17.         return cur_node;  
  18.     }  
  19. }  

五、返回最小关键字节点

根据二叉排序树的性质可知,关键字最小的节点一定是整棵树中最左边的节点所以只需从根节点开始一直寻找left指

针,直到找到NULL为止。

代码:

[objc]  view plain  copy
  1. //返回最小关键字所在节点  
  2. STnode *STree_Min(STnode *tree)  
  3. {  
  4.     STnode *cur_node=tree;  
  5.       
  6.     while(cur_node->left)  
  7.     {  
  8.         cur_node=cur_node->left;  
  9.     }  
  10.     return cur_node;  
  11. }  

六、返回最大关键字所在节点

根据二叉排序树的性质可知,关键字最大的节点一定是整棵树中最右边的节点所以只需从根节点开始一直寻找right

指针,直到找到NULL为止。

代码:

[objc]  view plain  copy
  1. //返回最大关键字所在节点  
  2. STnode *STree_Max(STnode *tree)  
  3. {  
  4.     STnode *cur_node=tree;  
  5.       
  6.     while(cur_node->right)  
  7.     {  
  8.         cur_node=cur_node->right;  
  9.     }  
  10.     return cur_node;  
  11. }  

七、查找节点后继(中序)

中序序列为:左子树、根、右子树,则一个节点若存在中序后继,则该后继必然位于该节点右边。

下图所示二叉排序树的中序为: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)

代码:

[objc]  view plain  copy
  1. STnode *STree_Successor(STnode *my_node)  
  2. {  
  3.     STnode *cur_node;  
  4.     STnode *successor;  
  5.     if(my_node->right)//右孩子不为空  
  6.         return STree_Min(my_node->right);  
  7.     else              //右孩子为空  
  8.     {  
  9.         if(my_node==my_node->p->left)//该节点为父节点左孩子  
  10.             return my_node->p;  
  11.         else//该节点为父节点右孩子  
  12.         {  
  13.             cur_node=my_node;  
  14.             successor=cur_node->p;  
  15.             while(successor!=NULL && cur_node!=successor->left)  
  16.             {  
  17.                 cur_node=cur_node->p;  
  18.                 successor=cur_node->p;  
  19.             }  
  20.             return successor;  
  21.         }  
  22.     }  
  23. }  

八、查找节点前驱(中序)

查找节点前驱的算法和查找节点后继的算法对称,也分为三种情况,分析分发一样,直接上代码。

代码:

[objc]  view plain  copy
  1. STnode *STree_Predecessor(STnode *my_node)  
  2. {  
  3.     STnode *cur_node;  
  4.     STnode *predecessor;  
  5.     if(my_node->left)//左孩子不为空  
  6.         return STree_Max(my_node->left);  
  7.     else              //左孩子为空  
  8.     {  
  9.         if(my_node==my_node->p->right)//该节点为父节点右孩子  
  10.             return my_node->p;  
  11.         else//该节点为父节点左孩子  
  12.         {  
  13.             cur_node=my_node;  
  14.             predecessor=cur_node->p;  
  15.             while(predecessor!=NULL && cur_node!=predecessor->right)  
  16.             {  
  17.                 cur_node=cur_node->p;  
  18.                 predecessor=cur_node->p;  
  19.             }  
  20.             return predecessor;  
  21.         }  
  22.     }  
  23. }  

九、删除节点

一、节点删除:如下图所示


(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有左孩子)


代码:(此处代码以个人理解的通俗方式书写,可能比较冗长,有较大优化空间)

[objc]  view plain  copy
  1. STnode *STree_Delete(STnode *tree, STnode *my_node)  
  2. {  
  3.     STnode *parent=my_node->p;  
  4.     STnode *suc_parent;  
  5.     STnode *successor;  
  6.       
  7.     if(my_node->left==NULL && my_node->right==NULL)//叶节点  
  8.     {  
  9.         if(parent==NULL)//只有一个节点  
  10.             return NULL;  
  11.               
  12.         if(my_node==parent->left)  
  13.             parent->left=NULL;  
  14.         else  
  15.             parent->right=NULL;  
  16.     }  
  17.     else if(my_node->left && my_node->right==NULL)//仅有左孩子  
  18.     {  
  19.         if(parent==NULL)//删除根节点  
  20.         {  
  21.             return tree->left;  
  22.         }  
  23.           
  24.         if(my_node==parent->left)  
  25.         {  
  26.             parent->left=my_node->left;  
  27.             my_node->left->p=parent;  
  28.         }  
  29.         else  
  30.         {  
  31.             parent->right=my_node->left;  
  32.             my_node->left->p=parent;  
  33.         }  
  34.     }  
  35.     else if(my_node->left==NULL && my_node->right)//仅有右孩子  
  36.     {  
  37.         if(parent==NULL)//删除根节点  
  38.         {  
  39.             return tree->right;  
  40.         }  
  41.           
  42.         if(my_node==parent->left)  
  43.         {  
  44.             parent->left=my_node->right;  
  45.             my_node->right->p=parent;  
  46.         }  
  47.         else  
  48.         {  
  49.             parent->right=my_node->right;  
  50.             my_node->right->p=parent;  
  51.         }  
  52.     }  
  53.     else//有两个孩子  
  54.     {  
  55.         successor=STree_Successor(my_node);  
  56.       
  57.         if(successor == my_node->right)//my_node后继为my_node->rchild  
  58.         {  
  59.             if(my_node==parent->left)  
  60.             {  
  61.                 parent->left=successor;  
  62.                 successor->p=parent;  
  63.                 successor->left=my_node->left;  
  64.             }  
  65.             else  
  66.             {  
  67.                 parent->right=successor;  
  68.                 successor->p=parent;  
  69.                 successor->left=my_node->left;  
  70.             }  
  71.         }  
  72.         else //my_node后继不为my_node->rchild  
  73.         {  
  74.             suc_parent=successor->p;  
  75.             suc_parent->left=successor->right;  
  76.             successor->right->p=suc_parent;  
  77.               
  78.             successor->right=my_node->right;  
  79.             successor->left=my_node->left;  
  80.             successor->p=my_node->p;            
  81.         }  
  82.     }  
  83.     if(parent==NULL)//删除根节点  
  84.         return successor;  
  85.      else  
  86.         return tree;  
  87. }  


二、主函数,测试用例及其头文件等

[objc]  view plain  copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. typedef struct STnode  
  4. {  
  5.     int key;//数据信息  
  6.     struct STnode *left;//指向左孩子  
  7.     struct STnode *right;//指向右孩子  
  8.     struct STnode *p;//指向父节点  
  9. } STnode;  
  10. STnode* STree_Insert(STnode *tree, STnode *my_node);  
  11. void STree_Inorder(STnode *tree);  
  12. STnode *STree_Creat(STnode *tree, int arr[], int n);  
  13. STnode *STree_Find(STnode *tree, int my_key);  
  14. STnode *STree_Max(STnode *tree);  
  15. STnode *STree_Min(STnode *tree);  
  16. STnode *STree_Successor(STnode *my_node);  
  17. STnode *STree_Predecessor(STnode *my_node);  
  18. STnode *STree_Delete(STnode *tree, STnode *my_node);  
  19.   
  20. int main(void)  
  21. {  
  22.     int i;  
  23.     int arr[]={15,6,20,3,7,17,21,4,13,19,9};  
  24.     STnode *tree=NULL;  
  25.     STnode *p;  
  26.        
  27.     tree=STree_Creat(tree, arr, 11);   
  28.       
  29.     STree_Inorder(tree);  
  30.     printf("\n");  
  31.       
  32.     p=STree_Find(tree, 7);  
  33.     printf("find the key: %d\n",p->key);  
  34.       
  35.     p=STree_Max(tree);  
  36.     printf("the max key: %d\n", p->key);  
  37.       
  38.     p=STree_Min(tree);  
  39.     printf("the min key: %d\n",p->key);  
  40.       
  41.     p=STree_Successor(STree_Find(tree, 7));  
  42.     printf("the successor of 7 is %d\n",p->key);  
  43.       
  44.     p=STree_Predecessor(STree_Find(tree, 7));  
  45.     printf("the predessor of 7 is %d\n",p->key);  
  46.       
  47.     //以下三组删除操作在同一颗树上连续进行;  
  48.     p=STree_Delete(tree, STree_Find(tree, 4));  
  49.     printf("delete the leaf node 4:");  
  50.     STree_Inorder(p);  
  51.     printf("\n");  
  52.       
  53.     p=STree_Delete(tree, STree_Find(tree, 20));  
  54.     printf("delete the node 20 which only have left child:");  
  55.     STree_Inorder(p);  
  56.     printf("\n");  
  57.       
  58.     p=STree_Delete(tree, STree_Find(tree, 7));  
  59.     printf("delete the node 7 which only have right child:");  
  60.     STree_Inorder(p);  
  61.     printf("\n");  
  62.       
  63.     //重置二叉排序树,实验删除节点6和15  
  64.     tree=NULL;  
  65.     tree=STree_Creat(tree, arr, 11);   
  66.       
  67.     p=STree_Delete(tree, STree_Find(tree, 6));  
  68.     printf("delete the node 6 which have two children:");  
  69.     STree_Inorder(p);  
  70.     printf("\n");  
  71.       
  72.     p=STree_Delete(tree, STree_Find(tree, 15));  
  73.     printf("delete the node 15 which have two children:");  
  74.     STree_Inorder(p);  
  75.     printf("\n");  
  76.       
  77.     return 0;  
  78. }  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值