• 二叉树基础


    很多小伙伴对于数据结构中的树,一知半解,干了差不多两年的开发,我对这块也是不清不楚的,最近工作空档期,简单的学习下二叉树

    概念啥的百度一堆,可以自行查看

    下面直接上代码(因工作中使用的是jdk11,可能会有一些区别,望见谅)

    /**
     * 二叉树
     *
     * @param <T>
     */
    public class BinaryTree<T extends Comparable<T>> {
        // 根节点
        private Entry<T> root;
        // 数据量
        private int size = 0;
    
        public BinaryTree() {}
    
        /**
         * 添加元素
         *
         * @param item 待添加元素
         * @return 已添加元素
         */
        public T put(T item){
            //每次添加数据的时候都是从根节点向下遍历
            Entry<T> t = root;
            size++;
            if (t == null){
                //当前的叉树树的为空,将新节点设置为根节点,父节点为null
                root = new Entry<>(item,null);
                return root.item;
            }
            //自然排序结果,如果传入的数据小于当前节点返回-1,大于当前节点返回1,否则返回0
            int ret = 0;
            //记录父节点
            Entry<T> p = t;
            while (t != null){
                //与当前节点比较
                ret = item.compareTo(t.item);
                p = t;
                //插入节点比当前节点小,把当前节点设置为左子节点,然后与左子比较,以此类推找到合适的位置
                if (ret < 0) {
                    t = t.left;
                    //大于当前节点
                }else if (ret > 0) {
                    t = t.right;
                }else {
                    //相等就把旧值覆盖掉
                    t.item = item;
                    size--;
                    return t.item;
                }
            }
            //创建新节点
            Entry<T> e = new Entry<>(item,p);
            //根据比较结果将新节点放入合适的位置
            if (ret < 0) {
                p.left = e;
            }else {
                p.right = e;
            }
            return e.item;
        }
    
        public void print(){
            midIterator(root);
        }
    
        /**
         * 中序遍历
         *
         * @param e
         */
        public void midIterator(Entry<T> e) {
            if (e != null) {
                midIterator(e.left);
                System.out.println(e.item + " ");
                midIterator(e.right);
            }
        }
    
        /**
         * 获取根节点
         *
         * @return 根节点
         */
        public Entry<T> getRoot() {
            return root;
        }
    
        /**
         * 前序遍历
         *
         * @param e 开始遍历元素
         */
        public void prevIterator(Entry<T> e) {
            if (e != null) {
                System.out.print(e.item + " ");
                prevIterator(e.left);
                prevIterator(e.right);
            }
        }
    
        /**
         * 后续遍历
         *
         * @param e 开始遍历元素
         */
        public void subIterator(Entry<T> e) {
            if (e != null) {
                subIterator(e.left);
                subIterator(e.right);
                System.out.print(e.item + " ");
            }
        }
    
        /**
         * 获取节点节点
         *
         * @param item 获取节点
         * @return 获取到的节点,可能为null
         */
        private Entry<T> getEntry(T item){
            Entry<T> t = root;
            int ret;
            //从根节点开始遍历
            for (;t != null;){
                ret = item.compareTo(t.item);
                if (ret < 0) {
                    t = t.left;
                }else if (ret > 0) {
                    t = t.right;
                }else {
                    return t;
                }
            }
            return null;
        }
    
        /**
         * 判断是否存在该元素
         *
         * @param item 查找元素
         * @return 结果
         */
        public boolean contains(T item) {
            return getEntry(item) != null;
        }
    
        /**
         * 删除元素
         * 删除元素如果细分的话,可以分为4中:没有子节点,只有左节点,只有右节点,有两个子节点
         * 1)没有子节点这种情况比较简单,直接删除就可以了
         * 2)只有左节点或右节点,这两种情况处理方式是一致的,只是方向相反,所以在一起讲了,
         * 将删除节点的父节点的左节点(右节点)指向删除节点的子节点,将左节点(右节点)指向删除节点的父节点
         * 3)有两个子节点,这种情况相对来说比较复杂一点:
         * 找到删除节点的前驱节点或后继节点,然后将前驱或后继节点的值赋给删除节点,然后将前驱或后继节点删除。本质是删除前驱或后继节点
         * 前驱节点的特点:
         * 1)删除的左子节点没有右子节点,那么左子节点即为前驱节点
         * 2)删除节点的左子节点有右子节点,那么最右边的最后一个节点即为前驱节点
         * 后继节点的特点:
         * 与前驱节点刚好相反,总是右子节点,或则右子节点的最左子节点;例如:删除节点为c ,那么前驱节点为 m,后继节点为n
         * a
         * /     
         * b          c
         * /          /  
         * d    e       f    g
         * /    /      /    / 
         *
         * @param item 删除元素       h   i  j  k   l   m n   o
         * @return 删除结果
         */
        public boolean remove(T item){
            //获取删除节点
            Entry<T> delEntry = getEntry(item);
            if (delEntry == null) {
                return false;
            }
            //删除节点的父节点
            Entry<T> p = delEntry.parent;
            size--;
            //情况1:没有子节点
            if (delEntry.left == null && delEntry.right == null){
                //删除节点为根节点
                if (delEntry == root){
                    root = null;
                }else {//非根节点
                    //删除的是父节点的左节点
                    if (delEntry == p.left){
                        p.left = null;
                    }else {//删除右节点
                        p.right = null;
                    }
                }
                //情况2:删除的节点只有左节点
            }else if (delEntry.right == null){
                Entry<T> lc = delEntry.left;
                //删除的节点为根节点,将删除节点的左节点设置成根节点
                if (p == null) {
                    lc.parent = null;
                    root = lc;
                } else {//非根节点
                    if (delEntry == p.left){//删除左节点
                        p.left = lc;
                    }else {//删除右节点
                        p.right = lc;
                    }
                    lc.parent = p;
                }
                //情况3:删除节点只有右节点
            }else if (delEntry.left == null){
                Entry<T> rc = delEntry.right;
                //删除根节点
                if (p == null) {
                    rc.parent = null;
                    root = rc;
                }else {//删除非根节点
                    if (delEntry == p.left) {
                        p.left = rc;
                    }else{
                        p.right = rc;
                    }
                    rc.parent = p;
                }
                //情况4:删除节点有两个子节点
            }else {//有两个节点,找到后继节点,将值赋给删除节点,然后将后继节点删除掉即可
                Entry<T> successor = successor(delEntry);//获取到后继节点
                delEntry.item = successor.item;
                //后继节点为右子节点
                if (delEntry.right == successor){
                    //右子节点有右子节点
                    if (successor.right != null) {
                        delEntry.right = successor.right;
                        successor.right.parent = delEntry;
                    }else {//右子节点没有子节点
                        delEntry.right = null;
                    }
                }else {//后继节点必定是左节点
                    successor.parent.left = null;
                }
                return true;
            }
            //让gc回收
            delEntry.parent = null;
            delEntry.left = null;
            delEntry.right = null;
            return true;
        }
    
        /**
         * 查找后继节点
         *
         * @param delEntry 删除节点
         * @return 后继节点
         */
        private Entry<T> successor(Entry<T> delEntry) {
            //r节点必定不为空
            Entry<T> r = delEntry.right;
            while (r.left != null) {
                r = r.left;
            }
            return r;
        }
    
        public int size(){
            return size;
        }
    
        public boolean isEmpty(){
            return size == 0;
        }
    
        public void clear(){
            clear(getRoot());
            root = null;
        }
    
        private void clear(Entry<T> e){
            if(e != null){
                clear(e.left);
                e.left = null;
                clear(e.right);
                e.right = null;
            }
        }
    
        static final class Entry<T extends Comparable<T>> {
            //保存的数据
            private T item;
            //左子树
            private Entry<T> left;
            //右子树
            private Entry<T> right;
            //父节点
            private Entry<T> parent;
    
            Entry(T item, Entry<T> parent) {
                this.item = item;
                this.parent = parent;
            }
        }
    
        public static void main(String[] args){
            BinaryTree<Integer> binaryTree = new BinaryTree<>();
            //放数据
            binaryTree.put(73);
            binaryTree.put(22);
            binaryTree.put(532);
            binaryTree.put(62);
            binaryTree.put(72);
            binaryTree.put(243);
            binaryTree.put(42);
            binaryTree.put(3);
            binaryTree.put(12);
            binaryTree.put(52);
    
            System.out.println("size: " + binaryTree.size());
            binaryTree.put(73);
            System.out.println("添加相同元素后的size: " + binaryTree.size());
            //判断数据是否存在
            System.out.println("数据是否存在:" + binaryTree.contains(12));
            //中序遍历
            System.out.print("中序遍历结果: ");
            binaryTree.midIterator(binaryTree.getRoot());
            System.out.println();
            //前序遍历
            System.out.print("前遍历结果: ");
            binaryTree.prevIterator(binaryTree.getRoot());
            System.out.println();
            //后序遍历
            System.out.print("后续遍历结果: ");
            binaryTree.subIterator(binaryTree.getRoot());
            //删除数据
            System.out.println();
            binaryTree.remove(62);
            System.out.println("删除数据后判断是否存在:" + binaryTree.contains(62));
            //清空二叉树
            binaryTree.clear();
            System.out.print("清空数据后中序遍历: ");
            binaryTree.midIterator(binaryTree.getRoot());
        }
    }
    

      运行结果:

    size: 10
    添加相同元素后的size: 10
    数据是否存在:true
    中序遍历结果: 3
    12
    22
    42
    52
    62
    72
    73
    243
    532

    前遍历结果: 73 22 3 12 62 42 52 72 532 243
    后续遍历结果: 12 3 52 42 72 62 22 243 532 73
    删除数据后判断是否存在:false
    清空数据后中序遍历:

      

  • 相关阅读:
    Java8新特性简介
    责任链模式
    Bean的生命周期
    APP中https证书有效性验证引发安全问题(例Fiddler可抓https包)
    程序员成长指南
    Go 代码性能优化小技巧
    Go slice 扩容机制分析
    一次 Go 程序 out of memory 排查及反思
    curl 常用操作总结
    Go benchmark 详解
  • 原文地址:https://www.cnblogs.com/changyuyao/p/13637480.html
Copyright © 2020-2023  润新知