看图说话之二叉排序树

一丶二叉排序树基本介绍

    二叉树是一种常见的树结构,二叉排序树是二叉树的一种特殊情况,其在二叉树的基础上施加了一种“顺序性”的约束,BST在排序和查找中具有广泛的运用下图就是一个常见的BST(Binary sort tree,二叉排序树 )。


图1 二叉排序树

二叉树是一种常见的树结构,二叉排序树是二叉树的一种特殊情况,其在二叉树对照图1所示的二叉排序树,可以给出BST的如下主要特点:

  • 结构上,BST是二叉树
  • 排序性上,对于BST的任意一个节点,节点值大于左子树上的所有节点值,小于右子树上的所有节点值。
    从上述BST两条主要的特点可以看出BST是递归定义的,其左子树和右子树均是BST树。了解了BST的基本特点之后,接下来总结BST的插入,删除,建树的基本操作原理。
二丶BST的插入操作

    假设待插入数组为: int [] A =new int []{1, 17},将该数组一次插入到图1所示的二叉排序树中(这次做一个特殊的说明,在插入数据中不存在重复的数据)。

  1. 插入元素1,首先和根节点比较,发现1比根节点8小, 接着和节点8的左节点比较,发现1还是小于7,接着继续和7的左节点6比较,1小于6,同时节点6左节点为null,不存在比较的可能了,此时节点1充当节点6的左节点,比较的过程和结果如下图所示。


    图2元素1的插入过程
  2. 在上图2的基础上继续插入元素17,重复和步骤1相同的过程。首先17和根节点9比较,17大于9,所以和节点9的右节点比较,17>15,继续和15的右节点比较发现,17<18,所以17和18的左节点比较,但是发现18的左节点为null,于是17找到了正确的位置,为了18的左节点。比较的过程和结果如下图所示


    图3元素17的插入过程

    依据上述的两个例子可以清晰的看到BST的插入过的基本实现,下面总结下BST插入过程的特点。

  • 插入过程是一个递归的过程,用递归的过程可以这样描述,如果元素小于根元素,则将元素插入左子树,如果元素大于根元素,则将元素插入右子树,如果子树为null,那么插入元素就是新的子树。利用伪代码可以如下实现。
public Node insert(element t,Node root){
                 if(root==null) new Node(t);
                 if(t<root.element){
                 root.left= insert(t,root.left);
                }else if(t>root.element){
                         root.right =insert(t,root.right);
                }else {
                       //在BST中找到了相等的节点,不做处理直接返回
                        return ;
                }
          }
  • BST树的插入过程的时间效率取决于查找过程中元素的比较次数,被查找元素的深度越深时间消耗越大,最坏的情况就是插入序列是排序的情况,这样树的深度是N,插入的时间消耗也是O(N),如果BST的树形是完全二叉树,那么其最大深度就是Log(N),那么插入序列的时间消耗就是Log(N)。最坏情况和最优情况图下图所示:


    图4 最优BST树形结构和最差BST树形
三丶BST树的删除操作

    BST树的删除操作相比较于BST树的插入操作,过程很相似,只是稍稍复杂一些,主要是分如下三种情况进行讨论。

  1. 被删除元素不存在左儿子和右儿子,其过程如下图所示


    图5定位被删除节点

    图6 删除节点

    在该种情况下被删除元素不存在儿子节点,直接用null代替被删除元素

  2. 被删除元素只存在一个儿子,其过程如下 图所示
    图7定位被删除节点

    图8删除节点

    在该种情况下,被删除节点只有一个儿子,直接由其儿子节点替代删除节点。
  3. 被删除节点有两个儿子,其主要过程如下所示三步完成。
    图9 定位被删除节点

    图10删除第二步

        如图9中所示,定位被删除节点后,如果该节点左右儿子都存在,那么找到该节点右子树中的最小节点,用该最小节点替代被删除节点
    图11 删除节点右子树最小节点。

         经过图8,图9,图10所表示的不步骤后,元素6被删除了且BST的顺序性并未改变。上述三个步骤的汇总如下图所示
    图12 被删除节点具有左儿子和右儿子的删除操作
  • 小小的讨论,当被删除节点没有儿子或者只存在一个儿子时的删除操作不会存在任何疑问,但是当被删除节点存在两个儿子时的删除操作存在一个问题:为了必须用右子树的最小节点来替代,不能用左子树的最大节点来替代吗?答案是可以的,这两种删除的方式随便选择一个。对于一个棵很大BST而言,如果采用右子树的删除操作,经过多次删除操作后,BST的树形可能偏向左,同理相反。

  • 二叉树删除操作是递归的实现,用java可以这样实现。

public Node delete(int element,Node root){
       //未定位到删除元素
       if(root==null) return null;
       intdelta = element-root.element;
       if(delta<0){
           root.left = delete(element,root.left);
       }elseif(delta>0){
           root.right =delete(element,root.right);
       }else{
           if(root.left!=null&&root.right!=null){
              Node node = findMin(root.right);
              root.element =node.element;
              root.right = delete(node.element,root.right);
           }else{
              root = root.left!=null?root.left:root.right;
           }
       }
       returnroot;
    }
四丶二叉树的建树操作。

    二叉树的建树操作很容易理解,建立一个N个节点BST操作,就是对元素执行N次的插入操作,每次插入操作的时间复杂度若为理想得Log(N),那么建树操作的时间复杂度就是Nlog(N)。

五丶BST的应用
  • BST如其名字可以用来进行排序,利用待排序数组建立一个BST树,假设建立结果如下。中序遍历该数组元素输出就是对数组排序的结果


    图13 BST

    中序遍历结果: 6->7->8->9->10->12->15->18,建树的时间复杂度是Nlog(N),中序遍历额时间复杂度是N,综合起来该种排序方法时间复杂度是Nlog(N)。

  • BST树可以用来查找,BST的查找操作就是删除操作中定位操作,时间复杂度是log(N),这里要特别注意,上述的log(N)的时间复杂度是建立在BST具有如图5所示的良好树形结构的前提下。

六丶BST树的java代码实现
 public class BinarySortTree {
    publicNode root = null;
    publicstatic void main(String []args){
       int[] elements = new int[]{5,4,9,3,2};
       BinarySortTree bst = new BinarySortTree();
       /****测试插入,建树和中序遍历操作***/
       bst.buildTree(elements);
       bst.midTrace();
       System.out.println();
       /****测数删除操作之无儿子***************/
    //  bst.delete(9);
    //  bst.midTrace();
       /****测数删除操作之只有一个儿子***************/
    //  bst.delete(4);
    //  bst.midTrace();
       /****测数删除操作只有两个儿子***************/
       bst.delete(5);
       bst.midTrace();
    }
    //节点类型定义
    publicstatic class Node{
       publicint element;
       publicNode left;
       publicNode right;
       publicNode(int element){
           this.element = element;
           left = null;
           right = null;
       }
    }
    //外部使用的插入方法
    publicvoid insert(int element){
       root=insert(element,root);
    }
    //内部使用的插入方法
    private  Node insert(int element,Node root){
       if(root==null)
           return new Node(element);
       intdelta = element-root.element;
       if(delta<0){
           root.left = insert(element,root.left);
       }elseif(delta>0){
           root.right = insert(element,root.right);
       }else{
           //不做处理
       }
       returnroot;
    }
    //外部使用的建树方法
    publicvoid buildTree(int [] elements){
       if(root==null){
           for(int i=0;i<elements.length;i++){
              insert(elements[i]);
           }
       }else{
           //若树以存在,则不能建
       }
    }
    publicvoid delete(int element){
       delete(element,root);
    }
    publicNode delete(int element,Node root){
       //未定位到删除元素
       if(root==null) return null;
       intdelta = element-root.element;
       if(delta<0){
           root.left = delete(element,root.left);
       }elseif(delta>0){
           root.right =delete(element,root.right);
       }else{
           if(root.left!=null&&root.right!=null){
              Node node = findMin(root.right);
              root.element =node.element;
              root.right = delete(node.element,root.right);
           }else{
              root = root.left!=null?root.left:root.right;
           }
       }
       returnroot;
    }
    //中序遍历二叉树
    publicvoid midTrace(){
       if(root!=null){
           print(root);
       }
    }
    privatevoid print(Node node){
       if(node!=null){
           print(node.left);
           System.out.print(node.element+" ");
           print(node.right);
       }
    }
    //找到该节点下子树的最小节点。
    publicNode findMin(Node node){
       if(node.left==null)
           return node;
       else
           return findMin(node.left);
    }
}

Reference:
[1] 数据结构与算法 jaba语言描述版
[2] 原文博客链接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容