二叉树结构

二叉树介绍

  在进行链表结构开发的过程之中会发现所有的数据按照首尾相连的状态进行保存,那么当要进行某一个数据查询的时候(判断该数据是否存在),这种情况下的时间复杂度是“O(n)”,如果说链表的数据量小的情况下,性能上是不会有很大差别的,而一旦数据量很大,这个时候时间复杂度就会很大程度上损耗程序的运行性能,因此数据的存储结构必然要做出改变,应该可以尽可能的减少检索次数为出发点进行设计。对于现在的数据结构而言,最好的性能就是“O(logn)”,所以要想实现它,就可以利用二叉树的结构来完成。
  如果要实现一棵树的结构的定义,那么需要去考虑数据的存储形式,在二叉树的是实现中,其基本的实现原理如下:取第一个数据为保存的根节点,小于该节点的数据放在该节点的左子树,而大于该节点的数据要放在该节点的右子树。


二叉树

  如果要进行数据检索的话,此时就需要进行每个节点的判断,但是它的判断是区分左右的,所以不会是整个结构都进行判断处理,所以它的时间复杂度就是“O(logn)”。
  而对于二叉树而言,在进行数据获取的时候也有三种形式:前序遍历(根-左-右)、中序遍历(左-根-右)、后序遍历(左-右-根),那么现在只是以中序遍历为主,则以上的数据进行中序遍历的时候最终的结果:10、20、25、30、38、50、80、100;

二叉树的基础实现

数据添加

  在实现二叉树的处理之中最为关键的问题在于数据的保存,而且数据由于涉及到对象比较的问题,那么一定要有比较器的支持,而这个比较器首选一定是Comparable,随后如果想要进行数据的保存,首先一定要有一个节点类,节点类中由于涉及数据保存问题,所以必须使用Comparable(可以区分大小);

import java.util.Arrays;
public class JavaApiDemo {
    public static void main(String[] args) throws Exception{
        BinaryTree<Person> tree=new BinaryTree();
        tree.add(new Person("张三",30));
        tree.add(new Person("李四",31));
        tree.add(new Person("王五",28));
        tree.add(new Person("赵六",38));
        tree.add(new Person("孙七",37));
        System.out.println(Arrays.toString(tree.toArray()));
        //[【Person类对象】姓名:王五、年龄:28, 【Person类对象】姓名:张三、年龄:30, 【Person类对象】姓名:李四、年龄:31, 【Person类对象】姓名:孙七、年龄:37, 【Person类对象】姓名:赵六、年龄:38]
    }
}
/**
 * 实现二叉树操作
 * @param <T> 要进行二叉树的实现
 */
class BinaryTree<T extends Comparable<T>>{
    private class Node{
        private Comparable<T> data;//存放Comparable,可以比较大小
        private Node parent;//父节点
        private Node left;//左子树
        private Node right;//右子树
        public Node(Comparable<T> data){//构造方法直接进行数据的存储
            this.data=data;
        }
        /**
         * 实现节点数据的适当位置的存储
         * @param newNode 创建的新节点
         * @throws IllegalArgumentException 保存的数据已存在
         */
        public void addNode(Node newNode)throws IllegalArgumentException {
            int rs=newNode.data.compareTo((T)this.data);
            if(rs==0){
                throw new IllegalArgumentException("添加失败,保存的数据已存在");
            }
            if(rs<0){//比当前节点数据小
                if(this.left==null){//判断是否有左子树
                    //当前没有左子树
                    this.left=newNode;//保存左子树
                    newNode.parent=this;//更新父节点
                }else{
                    //需要向左边继续判断
                    this.left.addNode(newNode);//继续向下判断
                }
            }else{
                //比根节点的数据大
                if(this.right==null){
                    //当前没有右子树
                    this.right=newNode;//保存左子树
                    newNode.parent=this;//更新父节点
                }else{
                    this.right.addNode(newNode);//继续向下判断
                }
            }
        }
        /**
         * 实现所有数据的获取处理,按照中序遍历的形式来完成
         */
        public void toArrayNode() {
            if(this.left!=null){
                this.left.toArrayNode();
            }
            BinaryTree.this.returnData[BinaryTree.this.foot++]=this.data;
            if(this.right!=null){
                this.right.toArrayNode();
            }
        }
    }
    //-------------------以下为二叉树的功能实现--------------
    private Node root;//根节点
    private int count;//数据个数
    private Object[] returnData;//返回的数据
    private int foot=0;//脚标控制
    /**
     * 进行数据的保存
     * @param data 要保存的数据内容
     * @throws NullPointerException 保存数据为空时抛出的异常
     * @throws IllegalArgumentException 保存的数据已存在
     */
    public void add(Comparable<T> data)throws NullPointerException,IllegalArgumentException{
        if(data==null){
            throw new NullPointerException("保存的数据不允许为空!");
        }
        //所有的数据本身不具备节点关系的匹配,那么一定要将其包装在Node类之中
        Node newNode=new Node(data);//包装节点
        if(this.root==null){//现在没有根节点,则将第一个节点作为根节点
            this.root=newNode;
        }else{
            this.root.addNode(newNode);//交由node类进行处理
        }
        this.count++;
    }
    /**
     * 以对象数组的形式返回全部数据,如果没有数据就返回null
     * @return 全部数据
     */
    public Object[] toArray(){
        if(this.count==0){
            return null;
        }
        this.returnData=new Object[this.count];//保存长度为数组长度
        this.foot=0;//脚标清零
        this.root.toArrayNode();//直接通过Node类负责
        return this.returnData;//返回全部的数据
    }
}
class Person implements Comparable<Person>{
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "【Person类对象】姓名:" + this.name + "、年龄:" + this.age;
    }

    @Override
    public int compareTo(Person person) {
        return this.age-person.age;//升序
//        return person.age-this.age;//降序
    }
}

  在进行数据添加的时候只是实现了节点关系的保存,而这种关系的保存后的结果就是所有的数据都是有序排列。

数据删除

  二叉树中数据删除操作是非常复杂的,因为在进行数据删除的时候需要考虑很多情况:
  root:根节点、isRoot:待删除节点是否为根节点
  removeNode:待删除节点、removeParentNode:待删除节点父节点
  parent:父节点、left:左子节点、right:右子节点
  isLeftNode:待删除节点是否为父节点的左子节点,true为左子节点,false为右子节点

  情况1、如果待删除节点没有子节点;

//不包含子节点
if(isRoot){
    this.root=null;
}else{
    removeNode.parent = null;//父节点断开引用
    if(isLeftNode){
        removeParentNode.left=null;
    }else{
        removeParentNode.right=null;
    }
}
情况1

  情况2、如果待删除节点只有一个左子节点;

//有左子树、没有右子树
if(isRoot){
    this.root=this.root.left;
}else{
    removeNode.left.parent=removeNode.parent;
    if(isLeftNode){
        removeParentNode.left=removeNode.left;
    }else{
        removeParentNode.right=removeNode.left;
    }
}
情况2

  情况3、如果待删除节点只有一个右子节点;

 //有右子树、没有左子树
if(isRoot){
    this.root=this.root.right;
}else{
    removeNode.right.parent=removeNode.parent;
    if(isLeftNode){
        removeParentNode.left=removeNode.right;
    }else{
        removeParentNode.right=removeNode.right;
    }
}
情况3

  情况4、如果待删除节点有两个子节点:

//有左子树和右子树,将右边节点中最左边的节点找到,改变其指向
Node moveNode=removeNode.right;//移动的节点
if(moveNode.hasLeft()){
    while (moveNode.hasLeft()){
        //还有左边的节点,一直向左找
        moveNode=moveNode.left;
    }
    //当前moveNode就是有节点的最小左节点
    if(moveNode.hasRight()){
        //如果后继节点有右子树,右子树将替换其原始的位置
        moveNode.parent.left=moveNode.right;
    }else{
        //后继节点不包含右子树
        moveNode.parent.left=null;
    }
    moveNode.right=removeNode.right;//改变原始的有节点的指向
}
moveNode.left=removeNode.left;
if(isRoot){
    this.root=moveNode;
}else{
    moveNode.parent=removeNode.parent;
    if(isLeftNode){
        removeParentNode.left=moveNode;
    }else{
        removeParentNode.right=moveNode;
    }
}
情况4

  下面通过具体的代码实现操作功能。

public class JavaApiDemo {
    public static void main(String[] args) throws Exception{
        BinaryTree<Person> tree=new BinaryTree();
        tree.add(new Person("张三",80));
        tree.add(new Person("李四",50));
        tree.add(new Person("王五",90));
        tree.add(new Person("赵六",30));
        tree.add(new Person("孙七",60));
        tree.add(new Person("周八",85));
        tree.add(new Person("吴九",95));
        tree.add(new Person("郑十",10));
        tree.add(new Person("刘一",55));
        tree.add(new Person("陈二",70));
        tree.remove(new Person("张三",80));
        Object[] arrays=tree.toArray();
        for (Object array:arrays){
            System.out.println(array);
        }
        /**
         * 【Person类对象】姓名:郑十、年龄:10
         * 【Person类对象】姓名:赵六、年龄:30
         * 【Person类对象】姓名:李四、年龄:50
         * 【Person类对象】姓名:刘一、年龄:55
         * 【Person类对象】姓名:孙七、年龄:60
         * 【Person类对象】姓名:陈二、年龄:70
         * 【Person类对象】姓名:周八、年龄:85
         * 【Person类对象】姓名:王五、年龄:90
         * 【Person类对象】姓名:吴九、年龄:95
         */
    }
}
/**
 * 实现二叉树操作
 * @param <T> 要进行二叉树的实现
 */
class BinaryTree<T extends Comparable<T>>{
    private class Node{
        private Comparable<T> data;//存放Comparable,可以比较大小
        private Node parent;//父节点
        private Node left;//左子树
        private Node right;//右子树
        public Node(Comparable<T> data){//构造方法直接进行数据的存储
            this.data=data;
        }
        public boolean hasLeft(){
           return left!=null;
        }
        public boolean hasRight(){
            return right!=null;
        }
        /**
         * 实现节点数据的适当位置的存储
         * @param newNode 创建的新节点
         * @throws IllegalArgumentException 保存的数据已存在
         */
        public void addNode(Node newNode)throws IllegalArgumentException {
            int rs=newNode.data.compareTo((T)this.data);
            if(rs==0){
                throw new IllegalArgumentException("添加失败,保存的数据已存在");
            }
            if(rs<0){//比当前节点数据小
                if(this.left==null){//判断是否有左子树
                    //当前没有左子树
                    this.left=newNode;//保存左子树
                    newNode.parent=this;//更新父节点
                }else{
                    //需要向左边继续判断
                    this.left.addNode(newNode);//继续向下判断
                }
            }else{
                //比根节点的数据大
                if(this.right==null){
                    //当前没有右子树
                    this.right=newNode;//保存左子树
                    newNode.parent=this;//更新父节点
                }else{
                    this.right.addNode(newNode);//继续向下判断
                }
            }
        }
        /**
         * 实现所有数据的获取处理,按照中序遍历的形式来完成
         */
        public void toArrayNode() {
            if(this.left!=null){
                this.left.toArrayNode();
            }
            BinaryTree.this.returnData[BinaryTree.this.foot++]=this.data;
            if(this.right!=null){
                this.right.toArrayNode();
            }
        }
        /**
         *  获取要删除的节点对象
         * @param data 比较的对象
         * @return 要删除的节点对象,
         */
        public Node findNode(Comparable<T> data) {
            int rs=data.compareTo((T)this.data);
            if(rs==0){
                return this;//查找到了
            }
            if (rs < 0) {
                if (this.left != null) {
                    return this.left.findNode(data);
                }
                return null;
            }
            if (this.right != null) {
                return this.right.findNode(data);
            }
            return null;

        }
    }
    //-------------------以下为二叉树的功能实现--------------
    private Node root;//根节点
    private int count;//数据个数
    private Object[] returnData;//返回的数据
    private int foot=0;//脚标控制
    /**
     * 进行数据的保存
     * @param data 要保存的数据内容
     * @throws NullPointerException 保存数据为空时抛出的异常
     * @throws IllegalArgumentException 保存的数据已存在
     */
    public void add(Comparable<T> data)throws NullPointerException,IllegalArgumentException{
        if(data==null){
            throw new NullPointerException("保存的数据不允许为空!");
        }
        //所有的数据本身不具备节点关系的匹配,那么一定要将其包装在Node类之中
        Node newNode=new Node(data);//包装节点
        if(this.root==null){//现在没有根节点,则将第一个节点作为根节点
            this.root=newNode;
        }else{
            this.root.addNode(newNode);//交由node类进行处理
        }
        this.count++;
    }
    /**
     * 以对象数组的形式返回全部数据,如果没有数据就返回null
     * @return 全部数据
     */
    public Object[] toArray(){
        if(this.count==0){
            return null;
        }
        this.returnData=new Object[this.count];//保存长度为数组长度
        this.foot=0;//脚标清零
        this.root.toArrayNode();//直接通过Node类负责
        return this.returnData;//返回全部的数据
    }
    /**
     * 执行数据的删除处理
     * @param data 需要删除的数据
     */
    public void remove(Comparable<T> data) {
        if(this.root==null){//根节点不存在
            return;
        }
        Node removeNode = this.root.findNode(data);
        if (removeNode == null) {
            return;
        }
        this.count--;
        Node removeParentNode=removeNode.parent;
        boolean isRoot=removeParentNode==null;
        Boolean isLeftNode=null;
        if(isRoot==false){
            isLeftNode=data.compareTo((T)removeParentNode.data)<0;
        }
        //找到要删除的节点信息了
        if (!removeNode.hasLeft() && !removeNode.hasRight()) {
            if(isRoot){
                this.root=null;
            }else{
                //不包含子节点
                removeNode.parent = null;//父节点断开引用
                if(isLeftNode){
                    removeParentNode.left=null;
                }else{
                    removeParentNode.right=null;
                }
            }
        } else if (removeNode.hasLeft() && !removeNode.hasRight()) {
            //有左子树、没有右子树
            if(isRoot){
                this.root=this.root.left;
            }else{
                removeNode.left.parent=removeNode.parent;
                if(isLeftNode){
                    removeParentNode.left=removeNode.left;
                }else{
                    removeParentNode.right=removeNode.left;
                }
            }
        } else if (!removeNode.hasLeft() && removeNode.hasRight()) {
            //有右子树、没有左子树
            if(isRoot){
                this.root=this.root.right;
            }else{
                removeNode.right.parent=removeNode.parent;
                if(isLeftNode){
                    removeParentNode.left=removeNode.right;
                }else{
                    removeParentNode.right=removeNode.right;
                }
            }
        }else{
            //有左子树和右子树,将右边节点中最左边的节点找到,改变其指向
            Node moveNode=removeNode.right;//移动的节点
            if(moveNode.hasLeft()){
                while (moveNode.hasLeft()){
                    //还有左边的节点,一直向左找
                    moveNode=moveNode.left;
                }
                //当前moveNode就是有节点的最小左节点
                if(moveNode.hasRight()){
                    //如果后继节点有右子树,右子树将替换其原始的位置
                    moveNode.parent.left=moveNode.right;
                }else{
                    //后继节点不包含右子树
                    moveNode.parent.left=null;
                }
                moveNode.right=removeNode.right;//改变原始的有节点的指向
            }
            moveNode.left=removeNode.left;
            if(isRoot){
                this.root=moveNode;
            }else{
                moveNode.parent=removeNode.parent;
                if(isLeftNode){
                    removeParentNode.left=moveNode;
                }else{
                    removeParentNode.right=moveNode;
                }
            }
        }
    }
}
class Person implements Comparable<Person>{
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "【Person类对象】姓名:" + this.name + "、年龄:" + this.age;
    }

    @Override
    public int compareTo(Person person) {
        return this.age-person.age;//升序
//        return person.age-this.age;//降序
    }
}

  可见,二叉树数据结构删除操作是非常繁琐的,所以如果不是必须的情况下,不建议进行删除操作。

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

推荐阅读更多精彩内容

  • 二叉树的主要性质 性质1:非空二叉树上叶子节点数等于双分支节点数加1。(使用总分支数=总节点数-1证明)性质2:二...
    sunpy阅读 399评论 0 0
  • 二叉树是一种特殊的树,树是我们常用数据结构。因二叉树拥有多种优良特性,所以在实际应用中使用非常广泛。 这里我们讨论...
    DKider阅读 1,057评论 0 2
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,646评论 0 13
  • 目录 1、什么是树 2、相关术语 3、二叉树 3.1、二叉树的类型 3.2、二叉树的性质 3.3、二叉树的结构 3...
    我哈啊哈啊哈阅读 2,529评论 0 10
  • 本文主要作为自己的学习笔记,并不具备过多的指导意义。 目录 基本概念 二叉树的重点 二叉树的遍历 实现先序遍历 实...
    kirito_song阅读 1,357评论 0 5