zoukankan      html  css  js  c++  java
  • 2019-04-09 java数据结构(五) 二叉树

    一、二叉树理解

    1、以前学习链表的时候,一个节点只有一个数据;现在学习的二叉树,一个节点好比有三个数据,分别为:根节点数据、左节点数据、右节点数据。

    2、这样的比喻对我来说比较好理解。

    3、一些示意图:图一是单个,图二是多个连接起来。

    图一:     

    图二:

    二、二叉树的代码实现(自己根据思想写的)

    public class BiTree {
        BiTreeNode root;
        BiTree(Integer root){
            this.root=new BiTreeNode(root);
        }
        BiTree(){
            this.root=null;
        }
        //树的节点类
        public class BiTreeNode {
            public Integer data;
            public BiTreeNode lchild,rchild,parent;
            public BiTreeNode(){}
            public BiTreeNode(Integer data){
                this();
                this.data=data;
                this.lchild=null;
                this.rchild=null;
                this.parent=null;
            }
            public BiTreeNode(Integer data,BiTreeNode lchild,BiTreeNode rchild,BiTreeNode parent){
                this(); 
                this.data=data;
                this.lchild=lchild;
                this.rchild=rchild;
                this.parent=parent;
            }
        }
        /*
         * 查找某个节点,我们必须从根节点开始遍历。
          ①、查找值比当前节点值大,则搜索右子树;
          ②、查找值等于当前节点值,停止搜索(终止条件);
          ③、查找值小于当前节点值,则搜索左子树;
         */
        public BiTreeNode find(Integer key){
            BiTreeNode curNode=this.root;
            while(curNode!=null){
                if((int)curNode.data>(int)key){
                    curNode=curNode.lchild;
                }
                else if((int)curNode.data<(int)key){
                    curNode=curNode.rchild;
                }
                else{
                    return curNode;
                }
            }
            return null;    
        }
        /*
         *  要插入节点,必须先找到插入的位置。与查找操作相似,由于二叉搜索树的特殊性,
         *  待插入的节点也需要从根节点开始进行比较,小于根节点则与根节点左子树比较,反之则与右子树比较,直到左子树为空或右子树为空,则插入到相应为空的位置,
         *  在比较的过程中要注意保存父节点的信息 及 待插入的位置是父节点的左子树还是右子树,才能插入到正确的位置。
         */
        public boolean insert(BiTree bt,Integer value){
            BiTreeNode curNode=bt.root;
            BiTreeNode newNode=new BiTreeNode(value);
            BiTreeNode parentNode=null;
            //判断当前是不是空树
            if(curNode==null){
                bt.root=newNode;
                return true;
            }
            while(curNode!=null){
                parentNode=curNode;
                //这里插入比较只支持int类型的
                if((int)curNode.data>(int)value){
                    curNode=curNode.lchild;
                    if(curNode==null){
                        parentNode.lchild=newNode;
                        newNode.parent=parentNode;
                        return true;
                    }
                }
                else{
                    curNode=curNode.rchild;
                    if(curNode==null){
                        parentNode.rchild=newNode;
                        newNode.parent=parentNode;
                        return true;
                    }
                }
            }
            return false;
        }
         /*
         * 查找最小结点:返回tree为根结点的二叉树的最小结点。
         */
        public BiTreeNode minimum(BiTreeNode tree) {
            if(tree==null)
                return null;
            while(tree.lchild!=null){
                tree=tree.lchild;
            }
            return tree;
        }
        /*
         * 找结点(x)的后继结点。即,查找"二叉树中数据值大于该结点"的"最小结点"。
         */
        public BiTreeNode successor(BiTreeNode x){
            if(x.rchild!=null){
                return minimum(x.rchild);
            }
            
            BiTreeNode y=x.parent;
            while((y!=null)&&(y.rchild==x)){
                x=y;
                y=y.parent;
            }
            return y;
        }
        /*
         * 三种情况:
         * 1、要删除节点无子节点
         * 2、有左节点或右节点
         * 3、左、右节点都有
         */
        private void deleteNoChild(BiTreeNode node ){
            
            //判断是不是根节点
            if(node.parent==null)
                root=null;
            //判断是左节点还是右节点,然后将他删除
            if(node==node.parent.lchild)
                node.parent.lchild=null;
            else{
                node.parent.rchild=null;
            }
        }
        private void deleteHaveOneChild(BiTreeNode node ){
            //判断是不是根节点
            if(node.parent==null){
                if(node.lchild!=null){
                    root=node.lchild;
                }
                else{
                    root=node.rchild;
                }
            }
            else{
                if(node==node.parent.lchild){
                    //判断有左节点,还是有右节点
                    if(node.lchild!=null){
                        //设置该节点的左孩子 为该节点的父节点的左孩子
                        node.parent.lchild=node.lchild;
                        //设置该节点的左孩子的父节点 为 该节点的父节点
                        node.lchild.parent =node.parent;
                    }
                    else{
                        node.parent.lchild=node.rchild;
                        node.rchild.parent =node.parent;
                    }
                }
                else{
                    //判断有左节点,还是有右节点
                    if(node.lchild!=null){
                        //设置该节点的左孩子 为该节点的父节点的右孩子
                        node.parent.rchild=node.lchild;
                        //设置该节点的左孩子的父节点 为 该节点的父节点
                        node.lchild.parent =node.parent;
                    }
                    else{
                        node.parent.rchild=node.rchild;
                        node.rchild.parent =node.parent;
                    }
                }
            }
        }
        //查找出要删除节点的后续节点,,将后续节点的值保留,  再将后续节点删除,再将后续节点的值赋值到要删除的节点的值上。(就是将问题改变成了上面的两种情况)
        private void deleteHaveLRChild(BiTreeNode node){
            //要删除节点的后继节点
            BiTreeNode successor = successor(node);
            node.data=successor.data;
            //后续节点是否为 待删除节点的右节点
            if(successor ==node.rchild){
                //后续节点没有子节点
                if(successor.lchild==null && successor.rchild==null){
                    node.rchild=null;
                    return ;
                }
                else{
                    //后续节点只有一个右节点
                    node.rchild=successor.rchild;
                    successor.rchild.parent=node;
                    return;
                }
            }
            
            if(successor.lchild==null && successor.rchild==null){
                successor.parent.lchild=null;
                return;
            }
            else{
                successor.parent.lchild=successor.rchild;
                successor.rchild.parent=successor.parent;
                return;
            }
        }
        public boolean delete(Integer key){
            BiTreeNode node=find(key);
            if(node!=null){
                //1、
                if(node.lchild==null && node.rchild==null){
                    deleteNoChild(node);
                }
                //2、
                else  if(node.lchild!=null && node.rchild!=null){
                    deleteHaveLRChild(node);
                }
                //3、
                else{
                    deleteHaveOneChild(node);
                }
                return true;
            }
            return false;
        }
        //前序遍历的递归方式
        public void preRootTraverse(BiTreeNode bt){
            if(bt==null)
                return;
            System.out.print(bt.data+" ");
            preRootTraverse(bt.lchild);
            preRootTraverse(bt.rchild);
        }
        //中序遍历:左子树——》根节点——》右子树
        public void inRootTraverse(BiTreeNode bt){
            if(bt==null)
                return;
            inRootTraverse(bt.lchild);
            System.out.print(bt.data+" ");
            inRootTraverse(bt.rchild);
        }
        //后续遍历
        public void posRootTraverse(BiTreeNode bt){
            if(bt==null)
                return;
            posRootTraverse(bt.lchild);
            posRootTraverse(bt.rchild);
            System.out.print(bt.data+" ");
        }
    }

    三、过程总结

      a、删除节点比较麻烦,节点有三种情况。

        1、待删除节点无子节点;2、待删除节点只有一个子节点;3、待删除节点有两个子节点(找到它的后继节点,然后将后继节点的值赋值给待删除节点,然后删除后继节点就好了,这时就会变成前面两种情况);

        2、自己画的图例:

        3、参考链接:https://blog.csdn.net/isea533/article/details/80345507

      b、根据删除节点的思想,自己写一遍就很容易理解网上简洁的代码了。  

    四、网上的完整二叉树代码(简洁版)

    public class BSTree<T extends Comparable<T>> {
    
        BSTNode<T> mRoot;    // 根结点
    
        public class BSTNode<T extends Comparable<T>> {
            T key;                // 关键字(键值)
            BSTNode<T> left;    // 左孩子
            BSTNode<T> right;    // 右孩子
            BSTNode<T> parent;    // 父结点
    
            public BSTNode(T key, BSTNode<T> parent, BSTNode<T> left, BSTNode<T> right) {
                this.key = key;
                this.parent = parent;
                this.left = left;
                this.right = right;
            }
    
            public T getKey() {
                return key;
            }
    
            public String toString() {
                return "key:"+key;
            }
        }
    
        public BSTree() {
            mRoot=null;
        }
    
        /*
         * 前序遍历"二叉树"
         */
        private void preOrder(BSTNode<T> tree) {
            if(tree != null) {
                System.out.print(tree.key+" ");
                preOrder(tree.left);
                preOrder(tree.right);
            }
        }
    
        public void preOrder() {
            preOrder(mRoot);
        }
    
        /*
         * 中序遍历"二叉树"
         */
        private void inOrder(BSTNode<T> tree) {
            if(tree != null) {
                inOrder(tree.left);
                System.out.print(tree.key+" ");
                inOrder(tree.right);
            }
        }
    
        public void inOrder() {
            inOrder(mRoot);
        }
    
    
        /*
         * 后序遍历"二叉树"
         */
        private void postOrder(BSTNode<T> tree) {
            if(tree != null)
            {
                postOrder(tree.left);
                postOrder(tree.right);
                System.out.print(tree.key+" ");
            }
        }
    
        public void postOrder() {
            postOrder(mRoot);
        }
    
    
        /*
         * (递归实现)查找"二叉树x"中键值为key的节点
         */
        private BSTNode<T> search(BSTNode<T> x, T key) {
            if (x==null)
                return x;
    
            int cmp = key.compareTo(x.key);
            if (cmp < 0)
                return search(x.left, key);
            else if (cmp > 0)
                return search(x.right, key);
            else
                return x;
        }
    
        public BSTNode<T> search(T key) {
            return search(mRoot, key);
        }
    
        /*
         * (非递归实现)查找"二叉树x"中键值为key的节点
         */
        private BSTNode<T> iterativeSearch(BSTNode<T> x, T key) {
            while (x!=null) {
                int cmp = key.compareTo(x.key);
    
                if (cmp < 0)
                    x = x.left;
                else if (cmp > 0)
                    x = x.right;
                else
                    return x;
            }
    
            return x;
        }
    
        public BSTNode<T> iterativeSearch(T key) {
            return iterativeSearch(mRoot, key);
        }
    
        /*
         * 查找最小结点:返回tree为根结点的二叉树的最小结点。
         */
        private BSTNode<T> minimum(BSTNode<T> tree) {
            if (tree == null)
                return null;
    
            while(tree.left != null)
                tree = tree.left;
            return tree;
        }
    
        public T minimum() {
            BSTNode<T> p = minimum(mRoot);
            if (p != null)
                return p.key;
    
            return null;
        }
    
        /*
         * 查找最大结点:返回tree为根结点的二叉树的最大结点。
         */
        private BSTNode<T> maximum(BSTNode<T> tree) {
            if (tree == null)
                return null;
    
            while(tree.right != null)
                tree = tree.right;
            return tree;
        }
    
        public T maximum() {
            BSTNode<T> p = maximum(mRoot);
            if (p != null)
                return p.key;
    
            return null;
        }
    
        /*
         * 找结点(x)的后继结点。即,查找"二叉树中数据值大于该结点"的"最小结点"。
         */
        public BSTNode<T> successor(BSTNode<T> x) {
            // 如果x存在右孩子,则"x的后继结点"为 "以其右孩子为根的子树的最小结点"。
            if (x.right != null)
                return minimum(x.right);
    
            // 如果x没有右孩子。则x有以下两种可能:
            // (01) x是"一个左孩子",则"x的后继结点"为 "它的父结点"。
            // (02) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有左孩子",找到的这个"最低的父结点"就是"x的后继结点"。
            BSTNode<T> y = x.parent;
            while ((y!=null) && (x==y.right)) {
                x = y;
                y = y.parent;
            }
    
            return y;
        }
    
        /*
         * 找结点(x)的前驱结点。即,查找"二叉树中数据值小于该结点"的"最大结点"。
         */
        public BSTNode<T> predecessor(BSTNode<T> x) {
            // 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
            if (x.left != null)
                return maximum(x.left);
    
            // 如果x没有左孩子。则x有以下两种可能:
            // (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
            // (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
            BSTNode<T> y = x.parent;
            while ((y!=null) && (x==y.left)) {
                x = y;
                y = y.parent;
            }
    
            return y;
        }
    
        /*
         * 将结点插入到二叉树中
         *
         * 参数说明:
         *     bst 二叉树
         *     z 插入的结点
         */
        private void insert(BSTree<T> bst, BSTNode<T> z) {
            int cmp;
            BSTNode<T> y = null;
            BSTNode<T> x = bst.mRoot;
    
            // 查找z的插入位置
            while (x != null) {
                y = x;
                cmp = z.key.compareTo(x.key);
                if (cmp < 0)
                    x = x.left;
                else
                    x = x.right;
            }
    
            z.parent = y;
            if (y==null)
                bst.mRoot = z;
            else {
                cmp = z.key.compareTo(y.key);
                if (cmp < 0)
                    y.left = z;
                else
                    y.right = z;
            }
        }
    
        /*
         * 新建结点(key),并将其插入到二叉树中
         *
         * 参数说明:
         *     tree 二叉树的根结点
         *     key 插入结点的键值
         */
        public void insert(T key) {
            BSTNode<T> z=new BSTNode<T>(key,null,null,null);
    
            // 如果新建结点失败,则返回。
            if (z != null)
                insert(this, z);//哪个树对象调用这个方法,就传该树对象进去
        }
        /*
         * 和下面的remover方法是一样的效果,这个方法更容易理解点,这个方法一定要好好看,very good
         */
        private BSTNode<T> delete(BSTNode<T> node) {
            //第 3 种情况,如果同时存在左右子节点
            if (node.left != null && node.right != null){
                //获取后继结点
                BSTNode<T> successor = successor(node);
                //转移后继结点值到当前节点
                node.key = successor.key;
                //把要删除的当前节点设置为后继结点
                node = successor;
            }
            //经过前一步处理,下面只有前两种情况,只能是一个节点或者没有节点
            //不管是否有子节点,都获取子节点
            BSTNode<T> child;
            if (node.left != null)
                child = node.left;
            else
                child = node.right;
            //如果 child != null,就说明是有一个节点的情况
            if (child != null)
                //将子节点和父节点关联上
                child.parent = node.parent;
            //如果当前节点没有父节点(后继情况到这儿时一定有父节点)
            //说明要删除的就是根节点
            if (node.parent == null)
                //根节点设置为子节点
                //按照前面逻辑,根只有一个或者没有节点,所以直接赋值 child 即可
                mRoot = child;
            else if (node == node.parent.left)//存在父节点,并且当前节点是左节点时
                //将父节点的左节点设置为 child
                node.parent.left = child;
            else//右节点时
                //将父节点的右节点设置为 child
                node.parent.right = child;
            //返回被删除的节点
            return node;
        }
        //删除指定的值
        public void delete(T key) {
            //获取要删除的节点
            BSTNode<T> node = search(mRoot, key);
            //如果存在就删除
            if (node != null)
                delete(node);
        }
    
        /*
         * 销毁二叉树
         */
        private void destroy(BSTNode<T> tree) {
            if (tree==null)
                return ;
    
            if (tree.left != null)
                destroy(tree.left);
            if (tree.right != null)
                destroy(tree.right);
    
            tree=null;
        }
    
        public void clear() {
            destroy(mRoot);
            mRoot = null;
        }
    
        /*
         * 打印"二叉查找树"
         *
         * key        -- 节点的键值
         * direction  --  0,表示该节点是根节点;
         *               -1,表示该节点是它的父结点的左孩子;
         *                1,表示该节点是它的父结点的右孩子。
         */
        private void print(BSTNode<T> tree, T key, int direction) {
    
            if(tree != null) {
    
                if(direction==0)    // tree是根节点
                    System.out.printf("%2d is root
    ", tree.key);
                else                // tree是分支节点
                    System.out.printf("%2d is %2d's %6s child
    ", tree.key, key, direction==1?"right" : "left");
    
                print(tree.left, tree.key, -1);
                print(tree.right,tree.key,  1);
            }
        }
    
        public void print() {
            if (mRoot != null)
                print(mRoot, mRoot.key, 0);
        }
    }
  • 相关阅读:
    阅读第十到十二章有感
    程序测试学习之5.2作业
    作业五——封装
    作业4 阅读《构建之法》第6 第7章有感
    汉堡包~~~
    作业3 阅读《构建之法》1-5章
    结对子实验——小学生四则运算
    小学生四则运算程序
    学会提问(转)
    error of “omission” and “commission”
  • 原文地址:https://www.cnblogs.com/mathlin/p/10676623.html
Copyright © 2011-2022 走看看