• 二叉查找树(四)


      接上一篇,让我们来继续讨论二叉查找树的基本操作,需要注意的一点是,上篇中的遍历操作是针对任意一棵二叉树都可以的,凡是没提及需要二叉查找树性质的地方,应该都是满足二叉树的操作。上篇主要讨论了二叉树的遍历操作,这篇,我们来讨论二叉查找树的查找、求最大最小值以及求前驱和后继等操作。

    • 查找

      二叉查找树的查找操作可以在O(h)时间内完成,其中h为树的高度。查找的算法很简单,根据二叉查找树的性质,我们先将要比较的元素跟根元素相比较,如果相等则返回,如果大于根结点的key,则继续在右子树中查找,如果小于根结点的key值,则在左子树中查找。这也跟插入过程类似,童鞋们可以想象一下,我就不画图了,画图很麻烦。

      下面的代码完成了在树根为x的树中查找关键字为k的元素,如果存在的话就返回其饮用,不存在,则返回null。

      递归查找的代码为:

     1 /**
     2      * 查找以x为根结点的树中key的值为k的结点,返回找到的结点或者null
     3      * @author Alfred
     4      * @param x 根结点
     5      * @param k 要查找的整数
     6      * @return 找到的结点或者null
     7      */
     8     private TreeNode treeSearch(TreeNode x, int k){
     9         if(x == null || k == x.getKey()){
    10             return x;
    11         }
    12         if(k < x.getKey()){
    13             return treeSearch(x.getLeft(), k);//查左子树
    14         }else{
    15             return treeSearch(x.getRight(), k);//查右子树
    16         }
    17     }

      非递归查找的代码为:

     

     1 /**
     2      * 非递归地查找以x为根结点的树中key的值为k的结点,返回找到的结点或者null
     3      * @author Alfred
     4      * @param x 根结点
     5      * @param k 要查找的整数
     6      * @return 找到的结点或者null
     7      */
     8     private TreeNode treeSearchNonrecursive(TreeNode x, int k){
     9         while(x != null && k != x.getKey()){
    10             if(k < x.getKey()){
    11                 x = x.getLeft();
    12             }else{
    13                 x = x.getRight();
    14             }
    15         }
    16         return x;
    17     }
    • 最大值

      根据二叉查找树的性质,树中的最大值一定是位于整棵树的最“右”边的右孩子,因为,二叉查找树的性质中说明了,右子树中的结点都大于或者等于父结点和左子树。所以求最大值是一个很简单的操作。代码如下:

     1 /**
     2      * 找以x为根结点的二叉查找树中的最大值
     3      * @author Alfred
     4      * @param x 根结点
     5      * @return 最大值结点或者null
     6      */
     7     public TreeNode treeMax(TreeNode x){
     8         while(x.getRight() != null){
     9             x = x.getRight();
    10         }
    11         return x;
    12     }
    • 最小值

      同理,根据二叉查找树的性质,最小值一定是位于整棵树中最“左”边的左孩子,因为,二叉查找树的性质的性质中说明了,左子树中的结点都小于或者等于父结点和右子树。所以跟求最大值对偶的代码如下:

     1 /**
     2      * 找以x为根结点的二叉查找树中的最小值
     3      * @author Alfred
     4      * @param x 根结点
     5      * @return 最小值结点或者null
     6      */
     7     public TreeNode treeMin(TreeNode x){
     8         while(x.getLeft() != null){
     9             x = x.getLeft();
    10         }
    11         return x;
    12     }
    • 后继

      由于在之前的博客里面数结点的定义形式,在处理的时候将树中相同的结点全都合并了起来,因此,位于树中不同位置的结点的key值肯定是不同的。因此,我们将求某一个结点后继结点的操作看成是求大于该结点key值的结点中key值最小的那个结点(有点绕。。。)。

      我们记结点x为输入结点,y为输出结点,即y结点是x结点的后继,在我们这里分两种情况进行讨论:

      1. x结点的右子树不为空。

        根据二叉查找树的性质,y肯定是位于x的右子树上,而且是x的右子树的最小值。

      2. x结点的右子树为空。

        如果存在后继y,则y是x的最低祖先结点,且y的左儿子也是x的祖先。晕了?说的简单些。如果x结点的右子树为空,则以x结点为最“右”孩子的子树t中,x结点一定是这个子树的最大值,根据二叉查找树的性质,只有当存在某一结点y,y的左子树恰好是t的时候,y才是x的后继。现在回去读一读这段开始的话,是不是容易理解多了。

      好了,了解了基本的算法,就让我贴上代码吧~

     1     /**
     2      * 找结点x的后继结点
     3      * @author Alfred
     4      * @param x 结点
     5      * @return x的后继结点或者null
     6      */
     7     public TreeNode treeSuccessor(TreeNode x){
     8         //第一种情况
     9         if(x.getRight() != null){
    10             return treeMin(x.getRight());
    11         }
    12         //第二种情况
    13         TreeNode tmpNode = x.getParent();
    14         while(tmpNode != null && x == tmpNode.getRight()){
    15             x = tmpNode;
    16             tmpNode = tmpNode.getParent();
    17         }
    18         return tmpNode;
    19     }

      这个算法就是按照上面提到的两种情况来实现的,时间复杂度为O(h),h为树的高度。

    • 前驱

      求结点的前驱的算法与后继的算法是对称的。其时间复杂度也是O(h)。在处理上也分为两种情况,我就直接上代码了,有心的童鞋们自己想一下吧~

     1 /**
     2      * 找结点x的前趋结点
     3      * @author Alfred
     4      * @param x 结点
     5      * @return x的前趋结点或者null
     6      */
     7     public TreeNode treePredecessor(TreeNode x){
     8         //第一种情况
     9         if(x.getLeft() != null){
    10             return treeMax(x.getLeft());
    11         }
    12         //第二种情况
    13         TreeNode tmpNode = x.getParent();
    14         while(tmpNode != null && x == tmpNode.getLeft()){
    15             x = tmpNode;
    16             tmpNode = tmpNode.getParent();
    17         }
    18         return tmpNode;
    19     }

      嗯。。。本话完,其他操作下回分解。

  • 相关阅读:
    移动端常用状态
    css 动画
    jQuery源码解析 -- 概述
    Bearer Token && JWT --- 深入理解令牌机制
    字符串 ----> switch-case 语句
    Vuejs选项: provide/inject
    本地windows系统-》windows云服务器文件上传
    CSS基础点
    函数的调用 与 this
    两个有意思的网站
  • 原文地址:https://www.cnblogs.com/unpolishedgem/p/2494731.html
Copyright © 2020-2023  润新知