zoukankan      html  css  js  c++  java
  • Android版数据结构与算法(八):二叉排序树

    本文目录

    前两篇文章我们学习了一些树的基本概念以及常用操作,本篇我们了解一下二叉树的一种特殊形式:二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。

    一、二叉排序树定义

    二叉排序树或者是一颗空树,或者是具有下列性质的二叉树:

    • 若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值

    • 若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值

    • 它的左,右子树也分别为二叉排序树

    也就是说二叉排序树中左子树结点值均小于根结点值,右子树节点值均大于跟节点值,左右子树同样满足上述约定。

    如下图,即为一颗二叉排序树:

    由二叉树定义知道,我们通过中序遍历二叉树就可以按照从小到大顺序排列二叉树中所有元素。

    如上图中序遍历结果为:35, 40, 42, 45, 50, 67

    二、java代码实现二叉排序树核心方法

    下面我们通过java代码实现二叉排序树中的几个核心方法

    我们先看下每个结点类定义如下:

     1class TreeNode{
     2        private int data;
     3        private TreeNode leftChild;
     4        private TreeNode rightChild;
     5        private TreeNode parent;
     6
     7        public TreeNode(int data) {
     8            this.data = data;
     9            this.leftChild = null;
    10            this.rightChild = null;
    11            this.parent = null;
    12        }
    13}

    很简单,每个结点记录自己以及左右孩子,父类的信息。

    二叉排序树的创建(增加元素方法)

    创建一颗二叉排序树就是不断往里面添加元素。 
    整体思路为:

    • 判断整棵树根结点是否创建过,如果没有创建那么第一个加入进来的元素指定为根结点,方法返回。

    • 如果二叉排序树已经创建过,那么再往里面加入元素需要先找出其父节点,然后将要插入的元素挂载到父节点下即可。

    • 经过上面过程找出其父结点,这里只需创建节点,挂载到父节点下即可,指定为父节点左孩子还是右孩子只需比较一下元素大小即可。

    源码:

     1    public TreeNode put(int data){
     2        TreeNode node = root;
     3        TreeNode parent = null;
     4        //判断二叉排序树根结点是否存在,不存在则创建
     5        if (root == null){
     6            root = new TreeNode(data);
     7            return root;
     8        }
     9        //查找其父类
    10        while (node != null){
    11            parent = node;//记录其父亲节点
    12            if (data > node.data){
    13                node = node.rightChild;
    14            }else if (data < node.data){
    15                node = node.leftChild;
    16            }else {
    17                //已经存在则直接返回
    18                return node;
    19            }
    20        }
    21        //创建新节点并插入原有树中
    22        node = new TreeNode(data);
    23        if (data < parent.data){
    24            parent.leftChild = node;
    25        }else {
    26            parent.rightChild = node;
    27        }
    28        node.parent = parent;
    29        return node;
    30    }
    二叉排序树的查找

    二叉排序树中查找比较简单,思路为:

    • 当前结点与查找的数据比较,相等则返回

    • 若小于当前结点则从左子树查找即可

    • 若大于当前结点则从右子树查找即可
      重复上述过程,这里就看出二分查找思想了

    源码:

     1    public TreeNode searchNode(int data) {
     2        TreeNode node = root;
     3        if (node == null){
     4            return null;
     5        }else {
     6            while (node != null && data != node.data){
     7                if (data < node.data){
     8                    node = node.leftChild;
     9                }else {
    10                    node = node.rightChild;
    11                }
    12            }
    13        }
    14        return node;
    15    }
    二叉排序树的删除

    二叉排序树的删除操作分4中情况:

    • 若要删除的结点无左右孩子也就是叶子结点,那么直接删除即可,将其父节点左或者右孩子置null即可

    • 若要删除的结点有左孩子无右孩子,则只需要将删除结点的左孩子与其父节点建立关系即可

    • 若要删除的结点有右孩子无左孩子,则只需要将删除结点的右孩子与其父节点建立关系即可

    • 若要删除的结点左右孩子均有,就需要选一个结点将其替换,这里需要保证选取的结点保证比左子树都大,右子树都小,可以选取左子树中最大的结点,或者右子树中最小的结点,并且需要将选取的结点从二叉排序树中删除。

    源码:这里我们选取右子树最小的结点

     1    public void deleteNode(int data){
     2        TreeNode node = searchNode(data);
     3        if (node == null){
     4            throw new RuntimeException("未找到要删除的节点");
     5        }else {
     6            delete(node);
     7        }
     8    }
     9
    10    private void delete(TreeNode node) {
    11        if (node == null){
    12            throw new RuntimeException("未找到要删除的节点");
    13        }else {
    14            TreeNode parent = node.parent;
    15            //删除的节点无左右孩子
    16            if (node.leftChild == null && node.rightChild == null){
    17                if (parent.leftChild == node){
    18                    parent.leftChild = null;
    19                }else {
    20                    parent.rightChild = null;
    21                }
    22                return;
    23            }
    24            //删除的节点有左无右
    25            if (node.leftChild != null
    26                    && node.rightChild == null){
    27                if (parent.leftChild == node){
    28                    parent.leftChild = node.leftChild;
    29                }else {
    30                    parent.rightChild = node.leftChild;
    31                }
    32                return;
    33            }
    34            //删除的节点有右无左
    35            if (node.leftChild == null
    36                    && node.rightChild != null){
    37                if (parent.leftChild == node){
    38                    parent.leftChild = node.rightChild;
    39                }else {
    40                    parent.rightChild = node.rightChild;
    41                }
    42                return;
    43            }
    44            //删除的结点左右都有
    45            TreeNode rightMinNode = getRightMinNode(node.rightChild);
    46            delete(rightMinNode);
    47            node.data = rightMinNode.data;
    48        }
    49    }
    50
    51    //获取右子树最小的结点
    52    private TreeNode getRightMinNode(TreeNode node) {
    53        TreeNode minNode = node;
    54        while (minNode != null && minNode.leftChild != null){
    55            minNode = minNode.leftChild;
    56        }
    57        System.out.println("minNode" + minNode.data);
    58        return minNode;
    59    }
    接下来我们测试一下

    测试代码:

     1 SearchBinaryTree ss = new SearchBinaryTree();
     2 int[] array = {77,88,34,55,66,2,34,67,78};
     3 for (int data : array) {
     4     ss.put(data);
     5 }
     6 ss.midIter(ss.getRoot());
     7 System.out.println();
     8 SearchBinaryTree.TreeNode node = ss.searchNode(66);
     9 System.out.println("find node:"+node.getData());
    10 ss.deleteNode(66);
    11 SearchBinaryTree.TreeNode dnode = ss.searchNode(66);
    12 if (dnode != null){
    13     System.out.println("find node:"+node.getData());
    14 }else {
    15     System.out.println("not find node");
    16 }
    17 ss.midIter(ss.getRoot());

    打印信息如下:

    1    2 34 55 66 67 77 78 88
    2    find node:66
    3    not find node
    4    2 34 55 67 77 78 88

    三、二叉排序树性能问题

    二叉排序树最好的情况下其查找性能是很高的,接近二分查找法。 
    但是在有些情况下构建出的二叉排序树类似一个链表,其查找性能为O(n),如下图: 

    构建出这样的树肯定不是我们希望的,需要调整此树达到平衡的效果,这里就需要二叉平衡树了(AVL树),关于AVL树会在后续篇章介绍,这里知道二叉平衡树有这个问题就可以了。

    四、总结

    本篇主要介绍了二叉平衡树以及Java代码实现其核心方法,希望你能掌握其与普通二叉树的区别,以及其存在的问题,好了,本片到此为止,希望对你有用。

    声明:文章将会陆续搬迁到个人公众号,以后也会第一时间发布到个人公众号,及时获取文章内容请关注公众号

    最后附上整个类的全部源码,拷贝过去就可以用了:

      1    public class SearchBinaryTree {
      2
      3    private TreeNode root;//二叉树根结点
      4
      5    public TreeNode getRoot() {
      6        return root;
      7    }
      8
      9    //中序遍历二叉排序树:按照从小到大排序
     10    public void midIter(TreeNode node){
     11        if (node == null){
     12            return;
     13        }
     14        midIter(node.leftChild);
     15        System.out.print(" "+node.data);
     16        midIter(node.rightChild);
     17    }
     18
     19    public TreeNode put(int data){
     20        TreeNode node = root;
     21        TreeNode parent = null;
     22        //判断二叉排序树根结点是否存在,不存在则创建
     23        if (root == null){
     24            root = new TreeNode(data);
     25            return root;
     26        }
     27        //查找其父类
     28        while (node != null){
     29            parent = node;//记录其父亲节点
     30            if (data > node.data){
     31                node = node.rightChild;
     32            }else if (data < node.data){
     33                node = node.leftChild;
     34            }else {
     35                //已经存在则直接返回
     36                return node;
     37            }
     38        }
     39        //创建新节点并插入原有树中
     40        node = new TreeNode(data);
     41        if (data < parent.data){
     42            parent.leftChild = node;
     43        }else {
     44            parent.rightChild = node;
     45        }
     46        node.parent = parent;
     47        return node;
     48    }
     49
     50    public void deleteNode(int data){
     51        TreeNode node = searchNode(data);
     52        if (node == null){
     53            throw new RuntimeException("未找到要删除的节点");
     54        }else {
     55            delete(node);
     56        }
     57    }
     58
     59    private void delete(TreeNode node) {
     60        if (node == null){
     61            throw new RuntimeException("未找到要删除的节点");
     62        }else {
     63            TreeNode parent = node.parent;
     64            //删除的节点无左右孩子
     65            if (node.leftChild == null && node.rightChild == null){
     66                if (parent.leftChild == node){
     67                    parent.leftChild = null;
     68                }else {
     69                    parent.rightChild = null;
     70                }
     71                return;
     72            }
     73            //删除的节点有左无右
     74            if (node.leftChild != null
     75                    && node.rightChild == null){
     76                if (parent.leftChild == node){
     77                    parent.leftChild = node.leftChild;
     78                }else {
     79                    parent.rightChild = node.leftChild;
     80                }
     81                return;
     82            }
     83            //删除的节点有右无左
     84            if (node.leftChild == null
     85                    && node.rightChild != null){
     86                if (parent.leftChild == node){
     87                    parent.leftChild = node.rightChild;
     88                }else {
     89                    parent.rightChild = node.rightChild;
     90                }
     91                return;
     92            }
     93            //删除的结点左右都有
     94            TreeNode rightMinNode = getRightMinNode(node.rightChild);
     95            delete(rightMinNode);
     96            node.data = rightMinNode.data;
     97        }
     98    }
     99
    100    private TreeNode getRightMinNode(TreeNode node) {
    101        TreeNode minNode = node;
    102        while (minNode != null && minNode.leftChild != null){
    103            minNode = minNode.leftChild;
    104        }
    105        System.out.println("minNode" + minNode.data);
    106        return minNode;
    107    }
    108
    109    public TreeNode searchNode(int data) {
    110        TreeNode node = root;
    111        if (node == null){
    112            return null;
    113        }else {
    114            while (node != null && data != node.data){
    115                if (data < node.data){
    116                    node = node.leftChild;
    117                }else {
    118                    node = node.rightChild;
    119                }
    120            }
    121        }
    122        return node;
    123    }
    124
    125
    126    public class TreeNode{
    127        private int data;
    128        private TreeNode leftChild;
    129        private TreeNode rightChild;
    130        private TreeNode parent;
    131
    132        public TreeNode(int data) {
    133            this.data = data;
    134            this.leftChild = null;
    135            this.rightChild = null;
    136            this.parent = null;
    137        }
    138
    139        public int getData() {
    140            return data;
    141        }
    142
    143        public void setData(int data) {
    144            this.data = data;
    145        }
    146
    147        public TreeNode getLeftChild() {
    148            return leftChild;
    149        }
    150
    151        public void setLeftChild(TreeNode leftChild) {
    152            this.leftChild = leftChild;
    153        }
    154
    155        public TreeNode getRightChild() {
    156            return rightChild;
    157        }
    158
    159        public void setRightChild(TreeNode rightChild) {
    160            this.rightChild = rightChild;
    161        }
    162
    163        public TreeNode getParent() {
    164            return parent;
    165        }
    166
    167        public void setParent(TreeNode parent) {
    168            this.parent = parent;
    169        }
    170    }
    171}
  • 相关阅读:
    JMeter性能测试
    CentOS7下安装elsticsearch、kibana、filebeat、logstash服务
    Docker容器 之 Harbor 私有镜像仓库的搭建
    Vagrant and VirtualBox 构建 CentOS7
    java 位运算 之 左移和右移理解
    List如果一边遍历,一边删除,且不会报错呢?
    享元模式
    装饰器模式
    日常记录
    销售与营销有什么区别?
  • 原文地址:https://www.cnblogs.com/leipDao/p/10058144.html
Copyright © 2011-2022 走看看