• 数据结构之二叉树


    数据结构分线性存储结构和非线性存储结构,前面说的顺序表,单链表,双链表,栈,队列都属于线性结构,线性结构的特别是集合中必存在唯一的一个"第一个元素,集合中必存在唯一的一个"最后的元素";除最后元素之外,其它数据元素均有唯一的"后继";除第一元素之外,其它数据元素均有唯一的"前驱"。大家注意的是唯一2个字,也就是说线性结构不可能存在2个或多个前驱结点,同样不可能出现2个或多个后继结点,现在我们说下2叉树,很明显是非线性的因为它有2个后继结点。

    一:树的基本概念

    父母结点:结点的前驱节点称为父母结点

    孩子结点:结点的后继节点称为孩子结点

    兄弟结点:拥有同一父母的结点称为兄弟结点

    度:结点拥有子树的的个数(后继节点的个数)没有后继结点的称为叶子结点(树的度指的是所有结点中度最大的)

    结点层次:指的是按照这个结点算起来的高度。

    树的高度:就是树的层次。

    二:二叉树结点类

    复制代码
    public class BinaryNode<T> {
        public T data;
        public BinaryNode<T> left;
        public BinaryNode<T> right;
    
        public BinaryNode(T data, BinaryNode<T> left, BinaryNode<T> right) {
            this.data = data;
            this.left = left;
            this.right = right;
        }
    
        public BinaryNode(T data) {//构建叶子节点
            this(data, null, null);
        }
    
        public BinaryNode() {
            this(null, null, null);
        }
    }
    复制代码

     三:树的抽象接口

    复制代码
    public interface BinaryTTree<T> {
        /**
         * 判断二叉树是否为null
         * @return
         */
        boolean isEmpty();
    
        /**
         * 返回二叉树节点个数
         * @return
         */
        int count();
    
        /**
         * 返回二叉树的高度
         * @return
         */
        int height();
    
        /**
         * 先根遍历
         */
        void preOrder();
    
        /**
         * 中根遍历
         */
        void inOrder();
    
        /**
         * 后跟遍历
         */
        void postOrder();
    
        /**
         * 层次遍历
         */
        void levelOrder();
    
        /**
         * 查找并返回首次出现关键字为key的元素节点
         * @param key
         * @return
         */
        BinaryNode<T> search(T key);
    
        /**
         * 返回弄得节点的父节点
         * @param node
         * @return
         */
        BinaryNode<T> getParent(BinaryNode<T> node);
    
        /**
         * 插入x作为根节点
         * @param x
         */
        void insertRoot(T x);
    
        /**
         * 插入p的孩子节点(leftchild验证为左子树还是右子树)
         * @return
         */
        BinaryNode<T> insertChild(BinaryNode<T> p,T x,boolean leftChild);
    
        /**
         * 删除p节点的左或者右子树
         * @param p
         * @param leftChild
         */
        void removeChild(BinaryNode<T> p,boolean leftChild);
    
        /**
         * 删除二叉树
         */
        void removeAll();
    }
    复制代码

    四:树的实现

    现在我先画一个很简单的树来帮助理解

    首先我们应该定义一个根,如果这个根空,则表示是空树。比如上面这颗树我们怎么计算他结点的个数呢,我们采取的方式是计算左子树的个数+右子树的个数+根不就是么,我们采用递归的思想来解决。

    复制代码
     public int count() {
            return count(this.root);
        }
        private int count(BinaryNode<T> node){
            if (node==null){
                return 0;
            }
            return 1+count(node.left)+count(node.right);
        }
    复制代码

    二叉树的高度其实道理一样,计算左子树高度和右子树高度取大的然后加上根即可

    复制代码
     public int height() {
            return height(this.root);
        }
    
        public int height(BinaryNode<T> root) {
            if (root == null) {
                return 0;
            }
            int lh = height(root.left);
            int rh = height(root.right);
            return lh >= rh ? lh + 1 : rh + 1;
        }
    复制代码

    先根遍历,就是先遍历根然后左子树然后右子树,比如上面的采用先根就是25,15,13,20,35,30,40.代码如下

    复制代码
      public void preOrder() {
            System.out.println("先根遍历开始:");
            preOrder(this.root);
            System.out.println();
        }
        public void preOrder(BinaryNode<T> p){
            if (p!=null){
                System.out.println(p.data.toString()+" ");
                preOrder(p.left);
                preOrder(p.right);
            }
        }
    复制代码

    中根是先执行左子树然后根最后右子树

    复制代码
     public void inOrder() {
            System.out.println("中根遍历开始:");
            inOrder(this.root);
            System.out.println();
        }
    
        public void inOrder(BinaryNode<T> p){
            if (p!=null){
                inOrder(p.left);
                System.out.println(p.data.toString()+" ");
                inOrder(p.right);
            }
        }
    复制代码

    后根是先左子树,然后右子树最后才根

    复制代码
    public void postOrder() {
            System.out.println("后跟遍历");
            postOrder(this.root);
            System.out.println();
        }
        public void postOrder(BinaryNode<T> p){
            if (p!=null){
                postOrder(p.left);
                postOrder(p.right);
                System.out.println(p.data.toString()+" ");
            }
        }
    复制代码

    查找也是如何,先从根比对,然后比对多有的左子树,如果没有找到在从右子树中查找

    复制代码
    public BinaryNode<T> search(T key) {
            return search(this.root, key);
        }
    
        public BinaryNode<T> search(BinaryNode<T> node,T key) {
            if (node == null || key == null) {
                return null;
            }
            if (node.data.equals(key)) {
                return node;
            }
            BinaryNode<T> find = search(node.left, key);
            if (find == null) {
                find = search(node.right, key);
            }
            return find;
        }
    复制代码
    复制代码
     public BinaryNode<T> getParent(BinaryNode<T> node) {
           if (this.root==null||node==null||this.root==node){
               return null;
           }
            return getParent(this.root,node);
        }
        //以p为根的子树中查找,并返回node节点的父母节点
        public BinaryNode<T> getParent(BinaryNode<T> p,BinaryNode<T> node){
            if (p.left==node||p.right==node){
                return p;
            }
            BinaryNode<T> find=getParent(p.left,node);
            if (find==null){
                find=getParent(p.right,node);
            }
            return find;
        }
    
        public void insertRoot(T x) {
            this.root = new BinaryNode<T>(x, this.root, null);
        }
    
    
        public BinaryNode<T> insertChild(BinaryNode<T> p, T x, boolean leftChild) {
            if (p==null||x==null){
                return null;
            }
            if (leftChild){
                p.left=new BinaryNode<T>(x,p.left,null);
                return p.left;
            }
            p.right=new BinaryNode<T>(x,null,p.right);
            return p.right;
        }
    
        public void removeChild(BinaryNode<T> p, boolean leftChild) {
            if (p != null) {
                if (leftChild) {
                    p.left = null;
                } else {
                    p.right = null;
                }
            }
        }
    
        public void removeAll() {
            this.root=null;
        }
    复制代码

    五:排序二叉树

    主要说二叉树的添加和删除

    首先第一步必须找到要插入节点的位置,也就是说找到这个即将插入这个结点的父结点

    复制代码
        if (this.root == null) {
                this.root = new BinaryNode<T>(x);
            } else {
                BinaryNode<T> p = this.root, parent = null;
                while (p != null) {
                    parent = p;
                    if (x.compareTo(p.data) == 0) {
                        return;
                    }
                    if (x.compareTo(p.data) < 0) {
                        p = p.left;
                    }else {
                        p=p.right;
                    }
                }
    复制代码

    我们先分析这段代码,如果是空树就和简单直接赋值即可,如果非空,我们分别申明2个变量,用parent来记录父结点,我们知道我们要插入的树已经是顺序的了,所以我们只要知道这个要插入的节点放在左边还是右边。我们找到了这个节点的父结点了就好办了。我们只需要和父结点比对即可,如果小就是左子树,如果打就是右子树

      p = new BinaryNode<T>(x);
                if (x.compareTo(parent.data) <= 0) {
                    parent.left = p;
                } else {
                    parent.right = p;
                }

    删除是相对比较复杂的,但是同样首先要找到要删除的结点p,然后在进行操作如下

    复制代码
      if (p==null){
                return null;
            }
            //首先找到删除节点p
            if (x.compareTo(p.data)<0){
                return remove(x,p.left,p);
            }
            if (x.compareTo(p.data)>0){
                return remove(x,p.right,p);
            }
    复制代码

    现在有几种情况,第一这个父结点左子树为null右子树不为null;第二右子树为null左子树不为null,第三都不为null。第一种情况我们只需要把要删除的p结点替换它的右子树即可,第二种情况我们同样把删除的p结点替换成它的左子树。第三种情况,既然删除父结点p我们只要需要把p结点的右子树中最小的结点赋值给这个删除的p结点即可,然后置空p结点右子树中的最小结点

    复制代码
     if (p.left!=null&&p.right!=null){
                BinaryNode<T> insucc=p.right;
                while (insucc.left!=null){
                    insucc=insucc.left;
                }//找到要删除节点的节点
                p.data=insucc.data;
                return remove(p.data,p.right,p);
            }
    复制代码

    然后我们考虑一下特别情况,比如这颗树 本身是叶子结点,或者度为1,那么直接进行转换就可以

    复制代码
    if (parent==null){
                if (p.left!=null){
                    root=p.left;
                }else {
                    root=p.right;
                }
                return p;
            }
    复制代码

    最后代码如下

    复制代码
    if (p==parent.left){
                if (p.left!=null){
                    parent.left=p.left;
                }else {
                    parent.left=p.right;
                }
            }else {
                if (p.left!=null){
                    parent.right=p.left;
                }else {
                    parent.right=p.right;
                }
            }
    复制代码
  • 相关阅读:
    Redis集群到集群迁移
    lvm磁盘创建
    前端开发环境
    golang Gorm 运用及执行原生SQL
    redis迁移两款工具
    C#知识点总结系列:3、C#中Delegate和Event以及它们的区别
    由浅入深CIL系列【目录索引】+ PostSharp AOP编程【目录索引】
    Windows 8实用窍门系列:22.Windows 8 的SemanticZoom缩放视图
    C#知识点总结系列:2、C#中IDisposable和IEnumerable、IEnumerator
    Windows 8实用窍门系列【目录索引】
  • 原文地址:https://www.cnblogs.com/Jansens520/p/6496506.html
Copyright © 2020-2023  润新知