zoukankan      html  css  js  c++  java
  • java:数据结构(四)二叉查找树以及树的三种遍历

    @

    二叉树模型

    二叉树是树的一种应用,一个节点可以有两个孩子:左孩子,右孩子,并且除了根节点以外每个节点都有一个父节点。当然这种简单的二叉树不能解决让树保持平衡状态,例如你一直往树的左边添加元素,就会导致查找效率的减慢。,如何解决这个问题,下一篇文章再说。

    二叉树的实现

    • 二叉树的实现类
    import java.util.LinkedList;
    
    /**
     * 二叉查找树
     * @param <E> 泛型节点
     */
    public class BinaryTree<E extends Comparable> implements Tree<E> {
        /**
         * @param root 根节点
         */
        private Node<E> root;
    
        /**
         * 内部类
         * 继承Comparable类来比较泛型的大小
         * 如果泛型是一个包含个多基本类型的对象,你需要重写compareTo方法
         * @param <E> 泛型类型的节点
         */
        private class Node<E extends Comparable> {
            E data;
            Node<E> left;
            Node<E> right;
    
            public void setLeft(Node<E> left) {
                this.left = left;
            }
    
            public void setRight(Node<E> right) {
                this.right = right;
            }
    
            public int getNum() {
                return num;
            }
    
            public void setNum(int num) {
                this.num = num;
            }
    
            int num;
            public Node<E> getLeft() {
                return left;
            }
    
    
    
            public Node<E> getRight() {
                return right;
            }
    
    
    
            public Node(E data){
                this.data=data;
            }
    
            public Node(E data, Node next, Node pre) {
                this.data = data;
                this.left = next;
                this.right = pre;
            }
    
            public Node(){
                left=null;
                right=null;
                data=null;
                num=1;
            }
            public E getData() {
                return data;
            }
    
            public void setData(E data) {
                this.data = data;
            }
    
    
    
        }
    
        /**
         * 判空方法
         * @return  true 树为空 false 树不为空
         */
        @Override
        public boolean isEmpty() {
            if(root==null){
                return true;
            }else{
                return false;
            }
        }
    
        /**
         * 使树为空
         * 个人理解:
         * 树中其他节点有实例对象存储在实例池中
         * 根节点不仅有实例对象,而且还要引用类型,存在java栈的本地变量表中
         * 之间使根节点为NULL
         */
        @Override
        public void makeEmpty() {
            root=null;
        }
    
        /**
         * 判断某个泛型类型的节点在树中
         * @param p 泛型类型
         * @return true 包含在节点中 false 不包含
         */
        @Override
        public boolean contains(E p) {
           return contain(p,root);
        }
    
        /**
         * contains方法的具体实现
         * 采用递归实现
         * @param p 需要判断是否存在书中泛型元素
         * @param r 树中的节点
         * @return true  包含 false 不包含
         */
        private boolean contain(E p,Node<E> r){
            Node<E> temp=root;
            if(root==null){
                return false;
            }else{
                if(temp.getData().compareTo(p)>0){
                    contain(p,temp.getLeft());
                }else if(temp.getData().compareTo(p)<0){
                    contain(p,temp.getRight());
                }else if(temp.getData().compareTo(p)==0&&temp.num>0){
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 找到树中最小的元素
         * @return 树中最小的元素
         */
        @Override
        public E findMin() {
            if(isEmpty()){
                System.out.println("树为空");
            }else {
                /**
                 * 因为采用懒惰删除,所以树中最小的元素不仅要是最左面的哪一个
                 * 而且还要是num>0的哪一个
                 */
                Node<E> p = root;
                Node<E> temp = root;
                while (p.getLeft() != null) {
                    p = p.getLeft();
                    if (p.num > 0) {
                        temp = p;
                    }
                }
                return temp.getData();
    
            }
            throw new NullPointerException();
        }
    
        /**
         * 找到树中最大的元素
         * 和寻找最小的原理一样
         * @return 树中最大的元素
         */
        @Override
        public E findMax() {
            if(isEmpty()){
                System.out.println("树为空");
            }else {
                Node<E> p = root;
                Node<E> temp = root;
                while (p.getRight() != null) {
                    p = p.getRight();
                    if (p.num > 0) {
                        temp = p;
                    }
                }
                return temp.getData();
            }
            throw new NullPointerException();
        }
    
        /**
         * 树的前序遍历
         * 根左右
         */
        @Override
        public void preTraversal() {
            System.out.print("前序遍历:");
            preTraversal(root);
            System.out.println();
        }
    
        /**
         * 前序遍历的具体实现方法
         * @param p 使用递归时进入下一个节点
         */
        private void preTraversal(Node<E> p) {
            if(isEmpty()){
                System.out.println("树为空");
            }else {
                if (p.num > 0) {
                    System.out.print(p.getData() + " ");
                }
                if (p.getLeft() != null) {
                    preTraversal(p.getLeft());
                }
                if (p.getRight() != null) {
                    preTraversal(p.getRight());
                }
                return;
            }
    
        }
    
        /**
         * 树的层序遍历
         * 即从根开始一层一层的遍历
         */
        public void seqTraverse(){
            if(isEmpty()){
                System.out.println("树为空");
            }else {
                System.out.print("层序遍历:");
                LinkedList<Node<E>> temp = new LinkedList<>();
                Node<E> p = root;
                temp.add(p);
                while (!temp.isEmpty()) {
                    Node<E> a = temp.pop();
                    if (a.num > 0) {
                        System.out.print(a.getData() + " ");
                    }
                    if (a.getLeft() != null) {
                        temp.add(a.getLeft());
                    }
                    if (a.getRight() != null) {
                        temp.add(a.getRight());
                    }
                }
                System.out.println();
            }
        }
    
        /**
         * 后序遍历的实现方法
         * @param p 使用递归时进入下一个节点
         */
        private void posTraversal(Node<E> p) {
            if(isEmpty()){
                System.out.println("树为空");
            }else {
                if (p.getLeft() != null) {
                    posTraversal(p.getLeft());
                }
                if (p.getRight() != null) {
                    posTraversal(p.getRight());
                }
                if (p.num > 0) {
                    System.out.print(p.getData() + " ");
                }
                return;
            }
        }
    
        /**
         * 中序遍历的实现方法
         * @param p 使用递归时进入下一个节点
         */
        private void cenTraversal(Node<E> p) {
            if(isEmpty()){
                System.out.println("树为空");
            }else {
                if (p.getLeft() != null) {
                    cenTraversal(p.getLeft());
                }
                if (p.num > 0) {
                    System.out.print(p.getData() + " ");
                }
                if (p.getRight() != null) {
                    cenTraversal(p.getRight());
                }
    
                return;
            }
        }
    
        /**
         * 树的后序遍历
         * 左右根
         */
        @Override
        public void posTraversal() {
            System.out.print("后序遍历:");
            posTraversal(root);
            System.out.println();
        }
        /**
         * 树的中序遍历
         * 左根右
         */
        @Override
        public void cenTraversal() {
            System.out.print("中序遍历:");
            cenTraversal(root);
            System.out.println();
        }
    
        /**
         * 向树中插入元素
         * @param p 待插入的元素
         */
        @Override
        public void insert(E p) {
            /**
             * 主要为了将num加一
             */
            Node temp=new Node<>(p);
            temp.num++;
            /**
             * 根为空,就给根赋值
             */
            if(root==null) {
                root=temp;
            }else{
                /**
                 *如果元素小于当前的d元素就往左递归
                 * 反之就向右递归
                 * 如果相等就num加一
                 */
                Node<E > d=root;
                while (d!=null){
                   if(p.compareTo(d.getData())<0){
                       if(d.getLeft()==null){
                           d.setLeft(temp);
                           break;
                       }
                        d=d.getLeft();
                   }else if(p.compareTo(d.getData())>0){
                       if(d.getRight()==null){
                           d.setRight(temp);
                           break;
                       }
                       d=d.getRight();
                   }else{
                       d.num++;
                   }
                }
            }
        }
    
        /**
         *删除树中的一个节点
         * 采用懒惰删除对num的节点进行减一
         * @param p 需要删除的内容
         */
        @Override
        public void remove(E p) {
            removePri(p,root);
        }
        private void removePri(E p,Node<E> r){
            Node temp=r;
            if(temp.getData().compareTo(p)>0){
                removePri(p,temp.getLeft());
            }else if(temp.getData().compareTo(p)<0){
                removePri(p,temp.getRight());
            }else {
                temp.num--;
            }
        }
    
    
    
    }
    
    
    • 接口的类
    /**
     * 树的接口
     * @param <E> 泛型的类型
     */
    public interface Tree<E > {
        /**
         * 判空函数
         * @return true 树为空 false 树不为空
         */
        boolean isEmpty();
    
        /**
         * 使树为空
         */
        void makeEmpty();
    
        /**
         * 检查书中是否包含p这个元素
         * @param p  泛型元素
         * @return true 包含 false 不包含
         */
        boolean contains(E p);
    
        /**
         * 找到树中最小元素
         * @return 最小的元素
         */
        E findMin();
        /**
         * 找到树中最大元素
         * @return 最大的元素
         */
        E findMax();
    
        /**
         * 前序遍历
         */
        void preTraversal();
    
        /**
         * 后序遍历
         */
        void posTraversal();
    
        /**
         * 中序遍历
         * 森林里没有中序遍历,森林无法判断哪个是中间
         * 所以可以不实现
         */
        default void cenTraversal() {
    
        }
    
        /**
         * 插入元素
         * @param p 待插入的元素
         */
        void insert(E p);
    
        /**
         * 删除方法
         * @param p 待删除的元素
         */
        void remove(E p);
    
    
    }
    
    

    对于代码的测试

    import java.util.Stack;
    public class Main {
        public static void main(String[] args) {
            BinaryTree<Integer> dd=new BinaryTree<>();
            dd.insert(4);
            dd.insert(2);
            dd.insert(3);
            dd.insert(1);
            dd.insert(6);
            dd.insert(5);
            dd.insert(7);
           // dd.preTraversal();
            //dd.cenTraversal();
          //  dd.posTraversal();
          //  dd.seqTraverse();
           System.out.println(dd.findMin()); ;
           System.out.println(dd.findMax());
            dd.remove(2);
            dd.remove(6);
        }
    }
    

    如上图的代码构成了如下的树:

    graph LR A((4)) --> B((2)) A --> C((6)) B-->d((1)) B-->e((3)) C-->g((5)) C-->j((7))
    • 调用findMax和findMin方法结果如下:
      在这里插入图片描述
    • 调用remove方法前后的区别
      -调用前在这里插入图片描述
      调用后:
      在这里插入图片描述
      可见被删除的元素没有输出

    树的三种遍历

    四种遍历分别是先序遍历、中序遍历、后序遍历、层序遍历

    先序遍历就是:对节点的除了工作是在它的诸儿子节点被处理前进行的,也就是说先访问根再访问左子树然后访问右子树。

    中序遍历:对节点的除了工作是在它的左儿子节点被处理后进行的,也就是说先访问左节点再访问根节点然后访问右节点。

    后序遍历:对节点的除了工作是在它的诸儿子节点被处理后进行的,也就是说先访问左再访问根子树然后访问右子树。

    层序遍历:和前三种遍历方式不一样,层序遍历需要借用队列,从根节点读入,然后出队,只要出队的元素有左节点,左节点就入队,若还有右节点,右节点就入队,直到队列为空。

  • 相关阅读:
    SharePoint 2013 安装.NET Framework 3.5 报错
    SharePoint 2016 配置工作流环境
    SharePoint 2016 站点注册工作流服务报错
    Work Management Service application in SharePoint 2016
    SharePoint 2016 安装 Cumulative Update for Service Bus 1.0 (KB2799752)报错
    SharePoint 2016 工作流报错“没有适用于此应用程序的地址”
    SharePoint 2016 工作流报错“未安装应用程序管理共享服务代理”
    SharePoint JavaScript API in application pages
    SharePoint 2016 每天预热脚本介绍
    SharePoint 无法删除搜索服务应用程序
  • 原文地址:https://www.cnblogs.com/lzy321/p/10802660.html
Copyright © 2011-2022 走看看