使用java实现顺序存储的二叉树

来源:互联网 发布:java static的作用 编辑:程序博客网 时间:2024/06/06 06:30

首先来看看什么是二叉树:二叉树一种所有结点的度都小于等于2的树。

需要写出二叉树就需要知道二叉树的一些性质:

1、二叉树的第i层最多有2^i个结点(最高层为第0层)

2、高度为h的二叉树最多有2^h-1个结点

3、对于一个给定的完全二叉树进行编号(从0开始),对于任意一个结点,其编号是i,则其左孩子标号是2i+1,右孩子的编号是2(i+1)(如果有的话);如果(i%2==0),则它的父结点的编号是i/2-1,如果(i%2!=0),则它的父节点的编号是(i-1)/2。

4、对于一个给定的编号i,它的层数计算方式为log2(i+1)向下取整(2为底,(i+1)的对数,其中编号从0开始,最高层为第0层),根据数学公式log2(i+1) = ln(i+1)/ln(2),因此可以使用Java中的Math.floor(Math.log(i+1)/Math.log(2))进行计算

5、遍历方式:

      广度优先遍历:一层一层从左到右进行遍历(借助队列)

      深度优先遍历:①先序遍历、每个结点的遍历都满足根结点、左结点、右结点(均借助栈)

                               ②中序遍历、每个结点的遍历都满足左结点、根结点、右结点

                               ③后序遍历、每个结点的遍历都满足左结点、右结点、根结点


二叉树常见的存储方式有两种:顺序存储和链式存储,对于非完全二叉树,顺序存储容易造成空间上的浪费,但是访问效率和修改效率比较高,链式存储则相反。

本次用Java实现顺序存储的二叉树(代码很冗长,可读性也比较糟糕...这点我承认):



首先创建一个接口Tree,规范二叉树的方法:

package com.arrayTree;import com.arrayTree.TreeNode;public interface Tree<E> {    boolean isEmpty();    E getRoot();    E getParent(int nodeNum);    E getLeftSibling(int nodeNum);    E getRightSibling(int nodeNum);    TreeNode<E> createNode(int headNum,E l, E r);    TreeNode<E> createHead(E item,E l, E r);    void breadFirstOrder();//广度优先    void preOrder();//先序遍历    void inOrder();//中序遍历    void postOrder();//后序遍历    void clear();//删除整个树}

然后创建树的结点类TreeNode<E>:

package com.arrayTree;public class TreeNode<E> {    private E item;    private E leftSibling;    private E rightSibling;    TreeNode(E item,E leftSibling, E rightSibling){//只有同包才能调用其构造方法        this.setItem(item);        this.setLeftSibling(leftSibling);        this.setRightSibling(rightSibling);    }    public E getItem() {        return item;    }    public void setItem(E item) {        this.item = item;    }    public E getLeftSibling() {        return leftSibling;    }    public void setLeftSibling(E leftSibling) {        this.leftSibling = leftSibling;    }    public E getRightSibling() {        return rightSibling;    }    public void setRightSibling(E rightSibling) {        this.rightSibling = rightSibling;    }}

创建ArrayTree<E>类,实现Tree<E>接口:

package com.arrayTree;import java.util.Arrays;import java.util.HashSet;import java.util.Set;import java.util.Stack;public class ArrayTree<E> implements Tree<E> {        public Object[] elementData;//存储元素的数组    public int level;//层数 最高层为第0层    public TreeNode<E> root;//根    private final static int DEFAULT_LEVEL = 10;//默认层数为10层    public ArrayTree(int level){        elementData = new Object[(2<<level)-1];//使用位运算代替乘法        this.level = level;    }    public ArrayTree(){        this(DEFAULT_LEVEL);    }    @Override    public boolean isEmpty() {        return elementData[0] == null;    }    @SuppressWarnings("unchecked")    @Override    public E getRoot() {        return (E) elementData[0];    }    @SuppressWarnings("unchecked")    @Override    public E getParent(int nodeNum) {        if(nodeNum%2 == 0){            return checkIndex((nodeNum>>1)-1)?(E)elementData[(nodeNum>>1)-1]:null;        }        else{            return checkIndex((nodeNum-1)>>1)?(E)elementData[(nodeNum-1)>>1]:null;        }    }    @SuppressWarnings("unchecked")    @Override    public E getLeftSibling(int nodeNum) {        return (checkIndex(nodeNum)&&checkIndex((nodeNum<<1)+1))?(E)elementData[(nodeNum<<1)+1]:null;    }    @SuppressWarnings("unchecked")    @Override    public E getRightSibling(int nodeNum) {        return (checkIndex(nodeNum)&&checkIndex((nodeNum+1)<<1))?(E)elementData[(nodeNum+1)<<1]:null;    }    @Override    public void breadFirstOrder() {        /*         * 广度优先遍历        * 直接顺序访问存储数组         * */        String str = "";        for(int i = 0 ; i<elementData.length ; i++){            if(elementData[i] != null)                str +=elementData[i]+" ";        }        System.out.println(str);    }    @Override    public void preOrder() {        /*         * 先序遍历:         * 遇到一个结点,先访问该结点,在遍历左子树,将右子树压入栈中,直到左子树遍历完,然后开始弹栈,对弹栈出来的结点继续像之前一样遍历,直到栈为空         * */        Stack<Integer> dataStack = new Stack<Integer>();//用栈来存放节点编号        int currentNum = 0;        String str = "";        while(!dataStack.isEmpty()||elementData[currentNum]!=null){//栈不为空或者当前节点不为空            if(checkIndex(currentNum)&&elementData[currentNum]!=null){//当前节点不为空                str += (elementData[currentNum]+" ");//输出当前节点                if(checkIndex((currentNum+1)<<1)&&elementData[(currentNum+1)<<1]!=null)//如果有右孩子                    dataStack.push((currentNum+1)<<1);//右孩子索引放入栈中                currentNum = (currentNum<<1)+1;//移向左孩子            }            else{                currentNum = dataStack.pop();//出栈 访问存入的右孩子            }        }        System.out.println(str);    }    @Override    public void inOrder() {        /*         * 中序遍历:         * 从根结点开始向左搜索,每遇到一个结点就把他压入栈中,然后取遍历这个结点的左子树,遍历完左子树后,开始弹栈,遍历弹出结点的右子树。         * */        Stack<Integer> dataStack = new Stack<Integer>();//用栈来存放节点编号        int currentNum = 0;        String str ="";        while(!dataStack.isEmpty()||elementData[currentNum]!=null){//栈不为空或者当前节点不为空            if(checkIndex(currentNum)&&elementData[currentNum]!=null){//当前节点不为空                dataStack.push(currentNum);//当前结点作为根节点入栈                currentNum = (currentNum<<1)+1;//转向左孩子            }            else{//节点为空了 当前路径访问到头了                currentNum = dataStack.pop();//出栈 访问存入的根结点                str += (elementData[currentNum]+" ");//访问根结点                currentNum = (currentNum+1)<<1;//当前结点切换为栈中根结点的右孩子            }        }        System.out.println(str);    }    @Override    public void postOrder() {        /*         * 后序遍历:         * 从根结点开始 向左搜索,每搜索到一个结点就将其压入栈中,直到栈中的结点不再有左子树为止。读取栈顶元素,如果该节点有右子树且未被访问,就访问其右子树,否则访问该节点并且弹栈         * */        Set<Integer> visitedSet = new HashSet<Integer>();//存放访问过的结点        int currentNum = 0;        Stack<Integer> dataStack = new Stack<Integer>();//用栈来存放编号        String str = "";        while(checkIndex(currentNum)&&elementData[currentNum]!=null){            while(checkIndex((currentNum<<1)+1)&&elementData[(currentNum<<1)+1]!=null){                dataStack.push(currentNum);//持续向左搜索 一旦遇到左结点为空 就停止搜索                currentNum = (currentNum<<1)+1;            }            //(当前结点不为空)且(没有右孩子或者右孩子被访问过了) 则访问该节点            while(checkIndex(currentNum) && elementData[currentNum]!=null &&                     (!checkIndex(((currentNum+1)>>1)) || elementData[(currentNum+1)<<1]==null||                    visitedSet.contains(elementData[(currentNum+1)<<1]))){                str += elementData[currentNum];                str +=" ";                visitedSet.add(currentNum);//添加进被访问过的集合                if(dataStack.isEmpty()){//栈空直接结束                    System.out.println(str);                    return;                }                currentNum = dataStack.pop();            }            dataStack.push(currentNum);            currentNum = (currentNum+1)<<1;//转向右子树        }    }    @Override    public void clear() {        root = null;        elementData = null;        level = 0;    }    public boolean checkIndex( int index ){//检查index这个索引会不会导致数组越界        return index>=0&&index<=((1<<(level+1))-2);//共level层时提供的编号的上下限    }        public void ensureCapacity(int level){//确保存储的数组有足够的容量        if(level>this.level){            if(elementData == null){                elementData = new Object[1];//为null就开辟一个            }            elementData  = Arrays.copyOf(elementData, (2<<level)-1);//用copyOf来扩容            this.level = level;        }    }        @Override    public TreeNode<E> createNode( int headNum,E l, E r) {        if(!checkIndex(headNum)||elementData[headNum]==null){//headNum没有            throw new IllegalArgumentException("头编号不存在");        }        if(l != null||r != null){//左右节点都有            if(checkIndex((headNum+1)<<1)&&level<10){//检查左右节点有没有足够level                ensureCapacity(10);//不到10层直接加到10层            }            else if(checkIndex((headNum+1)<<1)&&level>=10){//检查左右节点有没有足够level                ensureCapacity((headNum+1)<<1);//到了10层还不够则每次不够加一层            }        }        @SuppressWarnings("unchecked")        TreeNode<E> tn = new TreeNode<E>((E) elementData[headNum],l,r);//如果后面是null 直接创建        elementData[(headNum<<1)+1] = l;        elementData[(headNum+1)<<1]=r;        return tn;    }    @Override    public TreeNode<E> createHead(E item, E l, E r) {        if(root!=null){            throw new IllegalArgumentException("已经有头了");        }        if(level<1){            ensureCapacity(10);        }        root = new TreeNode<E>(item, l, r);        elementData[0] = item;        elementData[1] = l;        elementData[2] = r;        return root;    }}

最后写一个测试类:

public class Test{    public static void main(String[] args) {        System.out.println("Tree test:");        ArrayTree<Integer> at = new ArrayTree<Integer>();        at.createHead(0, 1, 2);        at.createNode(1, 3, 4);        at.createNode(2, 5, 6);        at.createNode(3, 7, 8);        at.breadFirstOrder();//0 1 2 3 4 5 6 7 8        at.preOrder();//0 1 3 7 8 4 2 5 6         at.inOrder();//7 3 8 1 4 0 5 2 6        at.postOrder();//7 8 3 4 1 5 6 2 0        System.out.println(at.level);        System.out.println(at.getParent(3));        System.out.println(at.getLeftSibling(0));        System.out.println(at.getRightSibling(-1));        at.clear();        at.createHead(1, 2, 3);    }}
虽然代码还有很大的改进空间...但是运行结果还算一切正常。欢迎在评论下面拍砖。