一、顺序存储
利用满二叉树的性质,每层的节点数1,2,4,8......,所以一颗深度为i的二叉树最多只能包含2 (i) - 1 (i是以指数的形式求出来的,此处利用等比数列求和),因此需要设置一个相同长度的数组来存储这个二叉树,若是空出来的节点,则数组元素留空即可。
参考代码:
public class ArrayBinaryTree<T> {
//使用数组来存储它的所有节点
private Object[] datas;
private int DEFAULT_DEEP = 10;
//记录该树的深度
private int deep;
private int arraySize;
//默认的深度创建一个二叉树
public ArrayBinaryTree(){
this.deep = DEFAULT_DEEP;
this.arraySize = (int)Math.pow(2, deep) - 1;//等比数列求和
datas = new Object[arraySize];
}
//以指定深度创建一个二叉树
public ArrayBinaryTree(int deep){
this.deep = deep;
this.arraySize = (int)Math.pow(2, deep) - 1;
datas = new Object[arraySize];
}
//以指定深度和指定的创建一个二叉树
public ArrayBinaryTree(int deep, T root){
this.deep = deep;
this.arraySize = (int)Math.pow(2, deep) - 1;
datas = new Object[arraySize];
datas[0] = root;
}
/**
* 为指定的节点添加子节点
* @param index 需要添加的子节点的父节点的索引
* @param data 新的节点的数据
* @param left 是否为左节点
*/
public void addNode(int index, T data, boolean left){
if(datas[index] == null){
throw new RuntimeException("空节点,无法添加!");
}
if(2 * index + 1 >= arraySize){
throw new RuntimeException("底层已满,越界异常!");
}
if(left){
datas[2 * index + 1] = data;
}
else{
datas[2 * index + 2] = data;
}
}
//判断是否为空
public boolean isEmpty(){
return datas[0] == null;
}
public String toString(){
return Arrays.toString(datas);
}
}
测试代码和截图:public class Test {
public static void main(String[] args) {
ArrayBinaryTree<String> binTree = new ArrayBinaryTree<String>(4, "root");
binTree.addNode( 0, "two_right", false);
binTree.addNode( 2, "three_right", false);
binTree.addNode( 6, "four_right", false);
System.out.println(binTree);
}
}
由此可以看出浪费存储资源。只有四个元素,分配了15长度的数组。(截图不完整)
二、二叉链表存储
让每个节点都记住它的左右两个子节点,因而增加了left, right两个指针,分别指向他们的左节点和右节点。
参考代码:
public class TwoLinkBinaryTree<E> {
public static class TreeNode{
Object data;
TreeNode left;
TreeNode right;
public TreeNode(){
}
public TreeNode(Object data){
this.data = data;
}
public TreeNode(Object data, TreeNode left, TreeNode right){
this.data = data;
this.left = left;
this.right = right;
}
}
private TreeNode root;
//默认的构造器创建一个二叉树
public TwoLinkBinaryTree(){
this.root = new TreeNode();
}
//以指定的根元素创建一个二叉树
public TwoLinkBinaryTree(E data){
this.root = new TreeNode(data);
}
/**
* 为指定的节点添加子节点
* @param parent 父节点
* @param data 新的节点的数据
* @param left 是否为左节点
* @return 新增加的节点
*/
public TreeNode addNode(TreeNode parent, E data, boolean left){
if(parent== null){
throw new RuntimeException("空节点,无法添加!");
}
if(left && parent.left != null){
throw new RuntimeException("已有左节点,无法添加!");
}
if(!left && parent.right != null){
throw new RuntimeException("已有右节点,无法添加!");
}
TreeNode newNode = new TreeNode(data);
if(left){
parent.left = newNode;//指向这个对象 而不是指向这个值
}
else{
parent.right = newNode;
}
return newNode;
}
//返回根节点
public TreeNode root() {
if(isEmpty()){
throw new RuntimeException("空树!");
}
return root;
}
//判断是否为空
private boolean isEmpty() {
return root.data == null;
}
//返回指定节点的(非叶子)的左节点 无则返回null
public E leftChild(TreeNode parent){
if(parent == null){
throw new RuntimeException("节点空,无法添加子节点!");
}
return parent.left == null ? null : (E)parent.left.data;
}
//返回指定节点的(非叶子节点)的右节点 无则返回null
public E rightChild(TreeNode parent){
if(parent == null){
throw new RuntimeException("节点空,无法添加子节点!");
}
return parent.right == null ? null : (E)parent.right.data;
}
}
测试部分及截图(增加了几个方法,让结果更有直观性):
代码:public class Test {
public static void main(String[] args) {
// ArrayBinaryTree<String> binTree = new ArrayBinaryTree<String>(4, "root");
TwoLinkBinaryTree<String> tlBinTree = new TwoLinkBinaryTree<>("root");
TwoLinkBinaryTree.TreeNode bt1 = tlBinTree.addNode(tlBinTree.root(), "two_left", true);
TwoLinkBinaryTree.TreeNode bt2 = tlBinTree.addNode(tlBinTree.root(), "two_right", false);
TwoLinkBinaryTree.TreeNode bt3 = tlBinTree.addNode(bt2, "three_right", false);
TwoLinkBinaryTree.TreeNode bt4 = tlBinTree.addNode(bt3, "four_right", false);
System.out.println("bt2的左节点:" + tlBinTree.leftChild(bt2));
System.out.println("bt2的右节点:" + tlBinTree.rightChild(bt2));
}
}
此方法遍历树节点时效率不高,指定节点访问其父节点也比较困难。但是节省空间。
三、三叉链表存储
就是在二叉链表的基础上,再增加一个parent指针,指向该节点的父节点。
代码与(二)类似,只需要在class TreeNode中添加一个parent, 构造函数里也做出改变。还有就是在addNode()方法中,新的节点newNode的parent应用需要指向parent节点。您可以对照(二)自己编写,相信比看要容易理解。
此时三叉链表存储的二叉树既可以方便的向下访问节点,也可以方便的向上访问节点。
参考:《疯狂java 突破程序员基本功的16课》