zoukankan      html  css  js  c++  java
  • JAVA实现二叉树

    树是编程中一种常用的数据结构。以前在学习数据结构时,总想着如何实际地实现出一颗二叉树出来,现在参考了《数据结构与算法分析 JAVA语言描述 第二版》之后,照着书中的例子实现了一颗二叉树,个人感觉书上面的二叉树实现操作比较复杂。下面将我学到的一些知识记录下来:

    1,定义树的操作的基本接口,其中不包括插入或删除操作,因为这二种操作与树的结构相关,不同的树的实现有着不同的插入与删除方式,故不应该将这二种操作放在接口中让所有的类来实现。同时,接口中也不包括遍历操作,因为并不是每个应用都会用到遍历。我们可以定义返回一个迭代器的方法,由于树中有多种不同的遍历,树的类可以含有几个方法,每个方法返回一种迭代器。

    基本接口中定义了树的常用操作,代码如下:

    public interface TreeInterface<T> {
        public T getRootData();
        public int getHeight();
        public int getNumberOfNodes();
        public boolean isEmpty();
        public void clear();
    }

    2,由于对树的许多操作都少不了遍历,因此需要构造一个能够对树中的元素进行遍历的迭代器。本例中采用内部类的方式来实现迭代器,下面的定义的接口 TreeIteratorInterface 包含了生成不同迭代器的方法。让实现树的具体的JAVA类 implements TreeIteratorInterface<T>,然后该JAVA类再定义一个内部类 implements Iterator<T>,即可构造一个能够对树中元素进行遍历的迭代器。

    为什么要让实现树的具体的JAVA类 implements TreeIteratorInterface<T>?

    因为,TreeIteratorInterface<T>接口中定义了返回各种各样迭代器的方法,实现树的具体的JAVA类 的对象就可以调用这些方法获得迭代器了。在后面的例子中,我们定义了一个具体实现树数据结构的类 BinaryTree<T>,该类 implements BinaryTreeInterface<T>,而 BinaryTreeInterface<T> 又extends TreeIteratorInterface<T>,因而BinaryTree<T>的对象可以调用 TreeIteratorInterface<T>接口中的方法来获得迭代器了。

    关于如何为自定义的数据结构实现迭代器,一个更好的参考例子:http://www.cnblogs.com/hapjin/p/4454594.html

    TreeIteratorInterface<T> 的具体代码如下:

    public interface TreeIteratorInterface<T> {
        public Iterator<T> getPreorderIterator();
        public Iterator<T> getPostorderIterator();
        public Iterator<T> getInorderIterator();
        public Iterator<T> getLevelOrderIterator();
    }

    3,定义了树的接口之后,由于我们大部分情况还是使用二叉树,因此下面定义二叉树的接口。首先,二叉树也是树,因此继承了TreeInterface<T>;其次,二叉树的对象要能够获得迭代器,因此又继承了TreeIteratorInterface<T>。(JAVA中接口允许多重继承)

    该二叉树接口没有什么特别的操作(大部分基本操作已经从TreeInterface<T>中继承下来了),它只定义了两个方法,这两个方法用来生成二叉树。

    //JAVA接口可以多继承
    public interface BinaryTreeInterface<T> extends TreeInterface<T> ,TreeIteratorInterface<T>{
        //以下这两个方法定义了如何构造二叉树
        public void setTree(T rootData);//构造一颗以rootData为根的二叉树
        //构造一颗以rootData为根,leftTree为左子树,rightTree为右子树的二叉树
        public void setTree(T rootData, BinaryTreeInterface<T> leftTree, BinaryTreeInterface<T> rightTree);
    }

    4,与单链表一样,链表中有链表结点,二叉树中也需要定义表示结点的类。而这里定义的表示结点的类采用独立的外部类实现,而不是采用内部类来实现。

    在考虑表示结点的类时,我们先定义了一个抽象的接口来规定对结点的一些基本操作。(这也是为什么我觉得书中二叉树实现比较复杂的原因)

    该接口为 BinaryNodeInterface<T>,具体代码如下:

    //二叉树结点的接口
    public interface BinaryNodeInterface<T> {
        public T getData();//返回结点的数据部分
        public void setData(T newData);//设置结点的数据域的值
        
        public BinaryNodeInterface<T> getLeftChild();//获取结点的左孩子
        public BinaryNodeInterface<T> getRightChild();//获取结点的右孩子
        
        public void setLeftChild(BinaryNodeInterface<T> leftChild);//设置结点的左孩子为指定结点
        public void setRightChild(BinaryNodeInterface<T> rightChild);//设置结点的右孩子为指定结点
        
        public boolean hasLeftChild();//判断结点是否有左孩子
        public boolean hasRightChild();//判断结点是否有右孩子
        
        public boolean isLeaf();//检查结点是否是叶子结点
        
        public int getNumberOfNodes();//计算以该结点为根的子树的结点数目
        public int getHeight();//计算以该结点为根的子树的高度
        public BinaryNodeInterface<T> copy();//复制以该结点为根的子树
    }

    5,接下来便是二叉树的节点的实现类了,该类 implements BinaryNodeInterface<T> 从而实现了接口中定义的各种对节点的操作。

    class BinaryNode<T> implements BinaryNodeInterface<T>, java.io.Serializable{
        private T data;//结点的数据域
        private BinaryNode<T> left;//左孩子
        private BinaryNode<T> right;//右孩子
        
        public BinaryNode(){
            this(null);
        }
        //构造一个值为dataPortaion的结点
        public BinaryNode(T dataPortion){
            this(dataPortion, null, null);
        }
        
        public BinaryNode(T dataPortion, BinaryNode<T> leftChild, BinaryNode<T> rightChild){
            data = dataPortion;
            left = leftChild;
            right = rightChild;
        }
        
        
        @Override
        //返回结点的数据域的值
        public T getData() {
            return data;
        }
    
        @Override
        //更改结点数据域的值
        public void setData(T newData) {
            data = newData;
            
        }
    
        @Override
        //获得结点的左孩子
        public BinaryNodeInterface<T> getLeftChild() {
            return left;
        }
    
        @Override
        //获得结点的右孩子
        public BinaryNodeInterface<T> getRightChild() {
            return right;
        }
    
        @Override
        //更改结点的左孩子
        public void setLeftChild(BinaryNodeInterface<T> leftChild) {
            left = (BinaryNode<T>)leftChild;
        }
    
        @Override
        //更改结点的右孩子
        public void setRightChild(BinaryNodeInterface<T> rightChild) {
            right = (BinaryNode<T>)rightChild;
        }
    
        @Override
        public boolean hasLeftChild() {
            return left != null;
        }
    
        @Override
        public boolean hasRightChild() {
            return right != null;
        }
    
        @Override
        public boolean isLeaf() {
            return (left == null) && (right == null);
        }
    
        @Override
        //返回以该结点为根的子树中的结点的个数(包括根结点)
        public int getNumberOfNodes() {
            int leftNumber = 0;
            int rightNumber = 0;
            if(left != null)
                leftNumber = left.getNumberOfNodes();
            if(right != null)
                rightNumber = right.getNumberOfNodes();
            return 1 + leftNumber + rightNumber;
        }
    
        @Override
        //返回以此结点为根的子树的高度
        public int getHeight() {
            return getHeight(this);
        }
        
        private int getHeight(BinaryNode<T> node){
            int height = 0;
            if(node != null)
                height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
            return height;
        }
    
        @Override
        //该方法被构造二叉树的setTree()方法调用
        public BinaryNodeInterface<T> copy() {
            BinaryNode<T> newRoot = new BinaryNode<T>(data);
            if(left != null)
                newRoot.left = (BinaryNode<T>)left.copy();
            if(right != null)
                newRoot.right = (BinaryNode<T>)right.copy();
            return newRoot;
        }
    }

    6,二叉树的节点也定义好了,是时候实现二叉树了(好麻烦啊啊啊)。上面已经谈到,BinaryTree<T>实现了一颗具体的二叉树,并且通过定义了一个内部类来生成迭代器。这里就简要介绍下这个实现迭代器的内部类:该内部类名为 private class InorderIterator<T> implements Iterator<T>

    这里,进行了一些偷懒,即下面的代码中只实现了能够对树进行中序遍历的迭代器即InorderIterator。当然了,你还可以再定义一个内部类PreorderIterator,然后按照先序遍历采用迭代的方法来实现先序遍历的迭代器。

    下面正式介绍私有内部类InorderIterator<T>,由于它 implements Iterator<T>,因此需要实现Iterator<T>中定义的三个抽象方法,这三个方法就是用来完成迭代的(遍历的)。我们按照中序遍历(非递归方式)的逻辑来实现这三个方法。中序遍历(非递归方式)需要栈来辅助存储结构,因此,代码中定义了一个顺序栈来保存遍历过程中遇到的结点,而该顺序栈的实现请参考另一篇博文:http://www.cnblogs.com/hapjin/p/4442729.html

    实现二叉树的BinaryTree<T>代码如下:

    import java.util.Iterator;
    import java.util.NoSuchElementException;
    
    import list.SequenceStack;//SequenceStack在list包中
    import list.Stack;//Stack接口在list包中
    
    public class BinaryTree<T> implements BinaryTreeInterface<T>, java.io.Serializable{
    
        private BinaryNodeInterface<T> root;//树根结点
    
        private class InorderIterator<T> implements Iterator<T>{
    
            //定义一个顺序栈nodeStack来存放遍历过程中遇到的结点
            private Stack<BinaryNodeInterface<T>>nodeStack;//list包中有顺序栈的实现
            private BinaryNodeInterface<T> currentNode;
            public InorderIterator(){
                nodeStack = new SequenceStack<BinaryNodeInterface<T>>();
                currentNode = (BinaryNodeInterface<T>) root;//此处为什么需要强制转换呢?
            }
            
            /*
             * 按照中序遍历的逻辑进行实现Iterator接口中的方法,从而实现一个迭代器
             */
            @Override
            public boolean hasNext() {
                return (!nodeStack.empty()) || (currentNode != null);
            }
    
            @Override
            public T next() {
                BinaryNodeInterface<T> nextNode = null;
                while(currentNode != null){
                    nodeStack.push(currentNode);
                    currentNode = currentNode.getLeftChild();
                }
                if(!nodeStack.empty()){
                    nextNode = nodeStack.pop();
                    assert nextNode != null;
                    currentNode = nextNode.getRightChild();
                }
                else
                    throw new NoSuchElementException();
                return nextNode.getData();
            }
    
            @Override
            //仅仅是完成遍历的功能,删除功能是不需要有的。
            public void remove() {
                throw new UnsupportedOperationException();
            }
            
        }
        
        public BinaryTree(){
            root = null;
        }
        public BinaryTree(T rootData){
            root = new BinaryNode<T>(rootData);
        }
        public BinaryTree(T rootData, BinaryTree<T> leftTree, BinaryTree<T> rightTree){
            privateSetTree(rootData, leftTree, rightTree);
        }
        
        @Override
        public void setTree(T rootData) {
            root = new BinaryNode<T>(rootData);
        }
    
        @Override
        /*
         *以rootData为根,leftTree为左子树,rightTree为右子树
         *生成一颗新的二叉树,setTree()实际调用了privateSetTree()来构造二叉树
         */
        public void setTree(T rootData, BinaryTreeInterface<T> leftTree,
                BinaryTreeInterface<T> rightTree) {
                privateSetTree(rootData, (BinaryTree)leftTree, (BinaryTree)rightTree);
        }
        private void privateSetTree(T rootData, BinaryTree<T>leftTree, BinaryTree<T> rightTree){
            root = new BinaryNode<T>(rootData);
            if((leftTree != null) && (!leftTree.isEmpty()))
                root.setLeftChild(leftTree.root);
            if((rightTree != null) && (!rightTree.isEmpty())){
                if(rightTree != leftTree)
                    root.setRightChild(rightTree.root);
                else
                    root.setRightChild(rightTree.root.copy());
            }
            if((leftTree != null) && (leftTree != this))
                leftTree.clear();
            if((rightTree != null) && (rightTree != this))
                rightTree.clear();    
        }
        
        //更改根结点的数据域
        protected void setRootData(T rootData){
            root.setData(rootData);
        }
        
        //更改根结点
        protected void setRootNode(BinaryNodeInterface<T> rootNode){
            root = rootNode;
        }
        protected BinaryNodeInterface<T> getRootNode(){
            return root;
        }
        
        @Override
        //返回树的根节点的数据域
        public T getRootData() {
            T rootData = null;
            if(root != null)
                rootData = root.getData();//调用节点的getData(),返回该节点的数据域
            return rootData;
        }
    
        @Override
        //返回二叉树的高度
        public int getHeight() {
            return root.getHeight();//二叉树的高度即为以根结点为根的子树的高度
        }
    
        @Override
        //返回二叉树中结点的个数
        public int getNumberOfNodes() {
            return root.getNumberOfNodes();
        }
    
        @Override
        public boolean isEmpty() {
            return root == null;
        }
    
        @Override
        public void clear() {
            root = null;
        }
    
        //中序遍历二叉树
        public void inorderTraverse(){
            inorderTraverse(root);
        }
        
        private void inorderTraverse(BinaryNodeInterface<T> node){
            if(node != null){
                inorderTraverse((BinaryNode)node.getLeftChild());
                System.out.println(node.getData());//若使用迭代器,可以在测试程序中输出,而不是在这里使用输出语句
                inorderTraverse((BinaryNode)node.getRightChild());
            }
        }
        
        public void preorderTraverse(){
            preorderTraverse(root);
        }
        
        private void preorderTraverse(BinaryNodeInterface<T> node){
            if(node != null){
                System.out.println(node.getData());
                preorderTraverse((BinaryNode)node.getLeftChild());
                preorderTraverse((BinaryNode)node.getRightChild());
            }
        }
        
        public void postorderTraverse(){
            postorderTraverse(root);
        }
        private void postorderTraverse(BinaryNodeInterface<T> node){
            if(node != null){
                postorderTraverse((BinaryNode)node.getLeftChild());
                postorderTraverse((BinaryNode)node.getRightChild());
                System.out.println(node.getData());
            }
        }
        @Override
        //获得先序遍历器的方法,由于在该类中没有定义生成先序迭代器的私有内部类,因此该方法为空实现
        public Iterator<T> getPreorderIterator() {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        //获得后序遍历器的方法,由于在该类中没有定义生成后序迭代器的私有内部类,因此该方法为空实现
        public Iterator<T> getPostorderIterator() {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public Iterator<T> getInorderIterator() {
            return new InorderIterator();
        }
    
        @Override
        //获得层次遍历器的方法,由于在该类中没有定义生成层次迭代器的私有内部类,因此该方法为空实现
        public Iterator<T> getLevelOrderIterator() {
            // TODO Auto-generated method stub
            return null;
        }
    }
  • 相关阅读:
    对数组对象处理及其他小问题
    前端面试题库
    题解 P3371 【【模板】单源最短路径】
    题解 P2403 【[DOI2010]所驼门王的宝藏】
    题解 P2283 【[HNOI2003]多边形】
    题解 P1074 【靶形数独 】
    题解 P1064 【金明的预算方案】
    题解 CH1813 【双栈排序】
    题解 CH1809 【匹配统计】
    题解 CH0805 【防线】
  • 原文地址:https://www.cnblogs.com/hapjin/p/4456785.html
Copyright © 2011-2022 走看看