package com.yc.tree;
import java.util.Arrays;
/**
* *
* @author WB
* @param <T>
*
* 顺序存储指的是充分利用满二叉树的特性:每层的节点数分别为1,2,4,8,……,2^(i-1),一棵深度为 i 的二叉树最多只能包含2^i - 1个节点,
* 因此只要定义一个长度为2^i - 1的数组即可存储这棵二叉树。
*
* 对于普通二叉树(不是满二叉树),那些空出来的节点对应的数组元素留空就可以了。由此可见,二叉树采用顺序存储会造成一定的空间浪费。
* 对于图11.6所示的二叉树(完全二叉树),采用图11.7所示的数组来保存即可。
*
* (A) ________________________
* │ │ │ │ │ │ │ │ │
* (B)←┘ └→(C) ——————> │ A │ B │ C │ D │ E │ F │
* │ │ │ │___│___│___│___│___│___│
* (D)←┘ └→(E) └→(F)
* 图11.6 完全二叉树 图11.7 二叉树的顺序存储
*
* 对于图11.8所示的二叉树,需用图11.9所示的数组来保存。
* (A)
* │ ____________________________________________________________
* └→(B) │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
* │ ——————> │ A │ │ B │ │ │ │ C │ │ │ │ │ │ │ │ D │
* └→(C) │___│___│___│___│___│___│___│___│___│___│___│___│___│___│___│
* │
* └→(D)
*
* 图11.8 普通二叉树 图11.9 二叉树的顺序存储
*
* ******************************************************************************
* 注意:当使用数组来存储二叉树的所有节点时有可能会产生一定的空间浪费,如果该二叉树是完全二叉树,就不会有任何浪费了;*
* 但如果该二叉树的所有节点都只有右子节点,那么就会产生相当大的空间浪费。如图11.9所示。 *
* *
* ******************************************************************************
*
* 从下面的介绍可以看出,顺序存储二叉树的思想就是将树中不同的节点存入数组的不同位置。比如,根节点,永远使用数组的第一个元素来存储它;
* 第二层的第二个节点,永远使用数组的第三个元素来保存;第三层最右边的节点,永远使用数组的第七个元素来保存……依次类推。
*
* 对于这种顺序存储的二叉树,不管是遍历树中节点,还是查找树中节点,都可以非常高效的完成,唯一的缺点是空间浪费很大。
*
*/
public class ArrayBinTree <T>{
private final int DEFAULT_DEEP = 8;
//底层数组
private Object[] datas;
//深度
private int deep;
private int arraySize;
public ArrayBinTree(){
deep = DEFAULT_DEEP;
arraySize = (int) Math.pow(2, deep) - 1;
datas = new Object[arraySize];
}
public ArrayBinTree(int deep){
this.deep = deep;
this.arraySize = (int) Math.pow(2, deep) - 1;
datas = new Object[arraySize];
}
public ArrayBinTree(T data, int deep){
this(deep);
datas[0] = data;
}
//判断二叉树是否为空
public boolean isEmpty(){
return datas[0] == null;
}
//返回根节点
@SuppressWarnings("unchecked")
public T root(){
return (T) datas[0];
}
/**
* 为指定节点添加新节点
* @param index :指定节点的索引位置
* @param data :数据元素
* @param left :是否是左子节点
*/
public void add(int index, T data, String flag){
exceptionDealAdd(index);
if(flag.intern().equals("left")){//添加左子节点
datas[2 * index + 1 ] = data;
}else if(flag.intern().equals("right")){//添加右子节点
datas[2 * index + 2 ] = data;
}
}
/**
//返回指定节点(非根节点)的父节点数据元素
*
* @param index :指定节点在数组中的索引
* @return
*/
@SuppressWarnings("unchecked")
public T parent(int index){
exceptionDealParent(index);
return (T) datas[(index - 1) / 2];
}
//返回指定节点的左子节点
@SuppressWarnings("unchecked")
public T left(int index){
exceptionDealLeft(index);
return (T) datas[2 * index + 1];
}
//返回指定节点的右子节点
@SuppressWarnings("unchecked")
public T right(int index){
exceptionDealRight(index);
return (T) datas[2 * index + 2];
}
//返回该树的深度
public int deep(){
return deep;
}
//返回指定节点(数据元素)的位置
public int pos(T data){
for(int i = 0; i < arraySize; i ++){
if(datas[i].equals(data) ){
return i;
}
}
return -1;
}
//toString方法
public String toString(){
return Arrays.toString(datas);
}
/*public void stringLikeTree(){
//StringBuffer sb = new StringBuffer();
for(int i = 1; i <= deep; i ++){
for(int j = (int) Math.pow(2, i - 1) - 1; j < (int) Math.pow(2, i) - 1; j ++){
System.out.print(datas[j]);
}
System.out.println();
}
}*/
private void exceptionDealAdd(int index){
if(datas[index] == null){
throw new RuntimeException(index + "处节点为空,无法添加新节点");
}
if(2 * index + 1 >= arraySize){
throw new RuntimeException("树底层数组已满,树越界异常");
}
}
private void exceptionDealParent(int index){
if(datas[index] == null){
throw new RuntimeException(index + "处节点为空,无法获得它的父节点");
}
if(index == 0){
throw new RuntimeException(index + "处节点为根节点,它无父节点");
}
}
private void exceptionDealLeft(int index){
if(datas[index] == null){
throw new RuntimeException(index + "处节点为空,无法获得它的左子节点");
}
if(2 * index + 1 >= arraySize){
throw new RuntimeException(index + "处节点无左子节点");
}
}
private void exceptionDealRight(int index){
if(datas[index] == null){
throw new RuntimeException(index + "处节点为空,无法获得它的右子节点");
}
if( 2 * index + 2 >= arraySize){
throw new RuntimeException(index + "处节点无右子节点");
}
}
}
测试代码如下:
public class ArrayBinTreeTest {
public static void main(String[] args) {
ArrayBinTree<String> tree = new ArrayBinTree<String>("根:aaaa", 4);
tree.add(0, "根节点的右子节点:bbbb", "right");
tree.add(0, "根节点的左子节点:cccc", "left");
tree.add(2, "第三层右子节点:dddd", "right");
tree.add(6, "第四层右子节点:eeee", "right");
System.out.println( tree);
}
}
测试结果如下:
[根:aaaa, 根节点的左子节点:cccc, 根节点的右子节点:bbbb, null, null, null, 第三层右子节点:dddd, null, null, null, null, null, null, null, 第四层右子节点:eeee]