• 二叉查找树5


    接上一篇,继续讲二叉查找树的操作,之前的博客都讲得差不多了,本篇就讲一下删除操作,以及求最矮公共父结点(LCA:lowest common ancestor)的操作吧。

    • 删除

      将一个结点从二叉查找树中删除之后,剩下的结点可能会不满足二叉查找树的性质,因此,在删除结点之后要对树进行调整,使其满足二叉查找树的性质。根据结点的孩子的数量,将删除操作分为三种情况,我们记要删除的结点为z,实际上删除的结点为y。

      1. z结点没有孩子。

      如下图a所示,我们要删除值为13的结点,因为结点没有孩子,所以删除之后不会影响到二叉树的整体性质,也就是说,直接将13这个结点删除即可,如图a所示,从左边的二叉树删除13这个点之后变到右边的二叉树。

       2. z结点有一个孩子。

      如下图b所示,要删除的值为16的结点有一个孩子,而且是右孩子,那么从图上来看,如果,我们将16去掉,然后把以20为结点的子树作为15的右子树,那么整棵树还是符合二叉查找树的性质的,因此,有一个孩子的结点的删除操作,就是要将其孩子作为其父结点的孩子即可。如图b所示。

       3. z结点有两个孩子。

      如下图c所示,要删除的值为5的结点,有两个孩子,删除之后肯定整棵树就不符合二叉查找树的性质了,因此要进行调整,我们发现,将5的后继,值为6的结点来放到5的位置,然后将6的孩子7作为6的父结点10的孩子,如下图c所示,我们要删除的是z结点,而我们实际要删除y结点,并替换z结点。这里需要注意的一点是,如果一个结点有右孩子,则该结点的后继,至多有一个子女,而且是右孩子。因为假如该结点的后继有左孩子和右孩子,那么其左孩子的值肯定是介于该结点和其后继之间的,那么按照二叉查找树的性质,这个左孩子就应该是该结点的后继,所以,这与原先的后继相互矛盾,因此,结论成立。

      好了,分析完了删除的三种情况,我们来完成我们的程序吧。

    复制代码
     1 /**
     2      * 删除二叉查找树中的结点z
     3      * @author Alfred
     4      * @param z 要删除的结点
     5      * @return 删除或者替换的结点
     6      */
     7     public TreeNode treeDelete(TreeNode z){
     8         TreeNode x = null, y = null;
     9         if(z.getLeft() == null || z.getRight() == null){
    10             //对应第1和第2种情况
    11             y = z;
    12         }else{
    13             //对应第3种情况
    14             y = treeSuccessor(z);
    15         }
    16         //将x置为y的非null子女,或者当y无子女时置为null
    17         if(y.getLeft() != null){
    18             x = y.getLeft();
    19         }else{
    20             x = y.getRight();
    21         }
    22         //通过修改x和y的父结点的引用,将y删除
    23         if(x != null){
    24             x.setParent(y.getParent());
    25         }
    26         if(y.getParent() == null){
    27             //x成为树根
    28             rootNode = x;
    29         }else if(y == y.getParent().getLeft()){
    30             //y是其父结点的左孩子
    31             y.getParent().setLeft(x);
    32         }else{
    33             //y是其父结点的右孩子
    34             y.getParent().setRight(x);
    35         }
    36         //如果y和z不是同一个结点,说明是第3种情况
    37         if(y != z){
    38             //内容替换
    39             z.setKey(y.getKey());
    40             z.setDataNum(y.getDataNum());
    41         }
    42         return y;
    43     }
    复制代码

      上面的程序完整的搞定了上面分析的三种情况。

    • LCA

      LCA问题对于二叉查找树来说是非常简单的。因为二叉查找树满足其特有的性质。给定树中的两个结点x和y,求其最低公共父结点。我们把这个算法分为三种情况。对于一棵二叉查找树来说:

      1. 如果x和y分别位于某结点z的左右子树上,那么结点z就是x和y的lca。

      2. 如果x和y位于某结点z的左子树或者右子树上,那么x和y的lca也必然位于其所处的左子树或者右子树上。

      3. 如果x或者y中,有一个结点是另外一个结点的父结点,那么lca就是它们中的父结点。即如果有一个点和某结点重合,则重合的点就是lca。

      那么,我们就从根结点开始,按照从上往下的顺序查找lca吧,比较根结点与两结点的关系,然后再进行下一步的操作。代码如下:

    复制代码
     1 /**
     2      * 求两个结点的最低公共父结点
     3      * @author Alfred
     4      * @param x 结点
     5      * @param y 结点
     6      * @return 最低公共父结点
     7      */
     8     public TreeNode lca(TreeNode x, TreeNode y){
     9         TreeNode tmpNode = rootNode;
    10         //获取两个key值
    11         int xKey = x.getKey();
    12         int yKey = y.getKey();
    13         //给两个元素按从小到大排下序,使得x<y
    14         if(xKey > yKey){
    15             int tmpKey = xKey;
    16             xKey = yKey;
    17             yKey = tmpKey;
    18         }
    19         while(true){
    20             if(tmpNode.getKey() < xKey){
    21                 //这种情况下,lca在其右子树上
    22                 tmpNode = tmpNode.getRight();
    23             }else if(tmpNode.getKey() > yKey){
    24                 //这种情况下,lca在其左子树上
    25                 tmpNode = tmpNode.getLeft();
    26             }else{
    27                 return tmpNode;
    28             }
    29         }
    30     }
    复制代码

      代码比较简单,就是按照上面的三种情况按照从上到下的顺序来分类处理的。其他还有一些lca算法,如离线算法(Tarjan)和在线算法(RMQ)等,暂时先不讨论了,以后有时间了再补上吧。为了方便想学习的童鞋来测试,我把整个代码就贴到下面了(木有找到更好地方法来分享。。)。

    TreeNode.java
    BSTree.java
    TestMain.java


      ps:关于二叉查找树的一些操作先写到这里吧,有不对的地方,请广大博友指正啊。

      pss:画图好累啊。。转载请注明。。。

     
     
    标签: 算法
  • 相关阅读:
    Mongoexport导出数据,Mongoimport导入数据,mongodump备份数据,mongorestore恢复恢复
    php7中使用mongodb的驱动
    windows(X64)+apche2.4+php7.2下安装mongodb
    windows(X64)下安装apche2.4+php7.2+mysql5.7
    Django2.0 path与Django1.x版本url正则匹配问题
    Django: ImportError: No module named 'corsheaders'
    linux开启端口
    MySQL 存储过程传参数实现where id in(1,2,3,...)示例
    ubuntu安装pip3
    在Ubuntu 16.04 安装python3.6 环境并设置为默认
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2497530.html
Copyright © 2020-2023  润新知