使用java实现顺序存储的二叉树
2017-11-17 17:18
513 查看
首先来看看什么是二叉树:二叉树一种所有结点的度都小于等于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,规范二叉树的方法:
然后创建树的结点类TreeNode<E>:
创建ArrayTree<E>类,实现Tree<E>接口:
最后写一个测试类:
需要写出二叉树就需要知道二叉树的一些性质:
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{ d526 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); } }虽然代码还有很大的改进空间...但是运行结果还算一切正常。欢迎在评论下面拍砖。
相关文章推荐
- Java使用顺序存储实现队列
- 二叉树的顺序存储和链式存储结构(java实现)
- 二叉树的顺序存储及其Java实现
- 二叉树的顺序存储实现(Java)
- 二叉树顺序存储(java实现)
- 使用java实现链式存储的二叉树
- 使用Java实现在SQLserver中实现图片的存储
- 二叉树基本操作实现(二叉树的顺序存储)
- 链表-顺序存储-Java实现
- 数据结构:二叉树的三叉链表存储--Java实现
- 二叉树的三叉链表存储及其Java实现
- 线性表java实现之顺序存储源码
- 线性表 顺序存储 链式存储 ---java实现
- java 实现线性表之顺序存储
- 线性表的Java实现--顺序存储
- 数据结构:线性表的顺序存储--Java实现
- 使用Java实现在SQLserver中实现图片的存储
- 使用Java实现在SQLserver中实现图片的存储
- 二叉树-链式存储-Java实现(未完待续)
- java实现线性表的顺序存储