二叉树的存储并非一个简单的问题. 引用 (指针) 是无法存储到文件里面的.
但我们从广度优先遍历的角度来思考似乎要简单些,每个节点都有一个 name 及其在二叉树中的位置. 令根节点的位置为 0; 则第 2 层节点的位置依次为 1 至 2; 第 3 层节点的位置依次为 3 至 6. 以此类推.
把昨天那个例子所对应的二叉树画出来, 我们有两种方法:
- 空使用 0 来表示, 可以用一个向量来存储:
[a, b, c, 0, d, e, 0, 0, 0, f, g]
优点: 仅需要一个向量, 简单直接.
缺点: 对于实际的二叉树, 很多子树为空, 导致大量的 0 值.
2.刘知鑫指出: 应使用压缩存储方式, 即将节点的位置和值均存储. 可表示为两个向量:
[0, 1, 2, 4, 5, 9, 10]
[a, b, c, d, e, f, g]
由于递归程序涉及变量的作用域问题 (额, 这个真的有点头疼), 我们需要使用层次遍历的方式, 获得以上的几个向量. 为此, 需要用到队列. 我们先灌点水, 把队列的代码再写一遍, 但这次装的是对象 (的引用)
package datastructure.queue;
/*
* Circle object queue.
@author Fan Min minfanphd@163.com
@learner ChenXun
*/
public class CircleObjectQueue {
/**
* The total space. One space can never be used.
*/
public static final int TOTAL_SPACE = 10;
/**
* The data.
*/
Object[] data;
/**
* The index of the head.
*/
int head;
/**
* The index of the tail.
*/
int tail;
/**
*******************
* The constructor
*******************
*/
public CircleObjectQueue() {
data = new Object[TOTAL_SPACE];
head = 0;
tail = 0;
}// Of the first constructor
/**
*********************
* Enqueue.
*
* @param paraValue The value of the new node.
*********************
*/
public void enqueue(Object paraValue) {
if ((tail + 1) % TOTAL_SPACE == head) {
System.out.println("Queue full.");
return;
} // Of if
data[tail % TOTAL_SPACE] = paraValue;
tail++;
}// Of enqueue
/**
*********************
* Dequeue.
*
* @return The value at the head.
*********************
*/
public Object dequeue() {
if (head == tail) {
// System.out.println("No element in the queue");
return null;
} // Of if
Object resultValue = data[head];
head++;
return resultValue;
}// Of dequeue
/**
*********************
* Overrides the method claimed in Object, the superclass of any class.
*********************
*/
public String toString() {
String resultString = "";
if (head == tail) {
return "empty";
} // Of if
for (int i = head; i < tail; i++) {
resultString += data[i % TOTAL_SPACE] + ", ";
} // Of for i
return resultString;
}// Of toString
/**
*********************
* The entrance of the program.
*
* @param args Not used now.
*********************
*/
public static void main(String args[]) {
CircleObjectQueue tempQueue = new CircleObjectQueue();
}// Of main
}// Of CircleObjectQueue``
- 数据转换的代码很短, 但要理解还是比较花时间. 反正我写的时候比较辛苦.
- 用了两个队列.
- 实际上涵盖了广度优先 (或称层次) 遍历.
- 节点左儿子的编号是其编号 2 倍再加 1; 右儿子则应加 2.
- main 前面的保留, 只是为了读者方便与前面的程序对标.
- 需要 import datastructure.queue.*;
代码如下:
package datastructure.tree;
import java.util.Arrays;
import datastructure.queue.*;
/**
* Binary tree with char type elements.
*
* @author Fan Min minfanphd@163.com
* @learner ChenXun
*/
public class BinaryCharTree1 {
// 字符中的值
char value;
// 左孩子
BinaryCharTree1 leftChild;
// 右孩子
BinaryCharTree1 rightChild;
// 构造方法
public BinaryCharTree1(char paraName) {
value = paraName;
leftChild = null;
rightChild = null;
}
// 手动建立一个二叉树
public static BinaryCharTree1 manualConstructTree() {
// 第一步,构造一个只有一个节点的树。
BinaryCharTree1 resultTree = new BinaryCharTree1('a');
// 第二步,构造所有的节点,第一个节点是根节点
// BinaryCharTreeNode tempTreeA = resultTree.root;
BinaryCharTree1 tempTreeB = new BinaryCharTree1('b');
BinaryCharTree1 tempTreeC = new BinaryCharTree1('c');
BinaryCharTree1 tempTreeD = new BinaryCharTree1('d');
BinaryCharTree1 tempTreeE = new BinaryCharTree1('e');
BinaryCharTree1 tempTreeF = new BinaryCharTree1('f');
BinaryCharTree1 tempTreeG = new BinaryCharTree1('g');
// 第三步,连接所有的节点
resultTree.leftChild = tempTreeB;
resultTree.rightChild = tempTreeC;
tempTreeB.rightChild = tempTreeD;
tempTreeC.leftChild = tempTreeE;
tempTreeD.leftChild = tempTreeF;
tempTreeD.rightChild = tempTreeG;
return resultTree;
}
// 前序遍历
public void preOrderVisit() {
System.out.print("" + value + " ");
if (leftChild != null) {
leftChild.preOrderVisit();
}
if (rightChild != null) {
rightChild.preOrderVisit();
}
}
// 中序遍历
public void inOrderVisit() {
if (leftChild != null) {
leftChild.inOrderVisit();
}
System.out.print("" + value + " ");
if (rightChild != null) {
rightChild.inOrderVisit();
}
}
// 后序遍历
public void postOrderVisit() {
if (leftChild != null) {
leftChild.postOrderVisit();
}
if (rightChild != null) {
rightChild.postOrderVisit();
}
System.out.print("" + value + " ");
}
// 获取二叉树的深度。
// 返回深度。如果只有一个节点,即根节点,则为 1。
public int getDepth() {
// 是叶子
if ((leftChild == null) && (rightChild == null)) {
return 1;
}
// 做孩子的深度
int tempLeftDepth = 0;
if (leftChild != null) {
tempLeftDepth = leftChild.getDepth();
}
// 右孩子的深度
int tempRightDepth = 0;
if (rightChild != null) {
tempRightDepth = rightChild.getDepth();
}
// 深度递增1
if (tempLeftDepth >= tempRightDepth) {
return tempLeftDepth + 1;
} else {
return tempRightDepth + 1;
}
}
// 获取节点数,并返回节点
public int getNumNodes() {
// 是叶子节点
if ((leftChild == null) && (rightChild == null)) {
return 1;
}
// 左孩子的节点
int tempLeftNodes = 0;
if (leftChild != null) {
tempLeftNodes = leftChild.getNumNodes();
}
// 右孩子的节点
int tempRightNodes = 0;
if (rightChild != null) {
tempRightNodes = rightChild.getNumNodes();
}
// 节点总数
return tempLeftNodes + tempRightNodes + 1;
}
/**
* 节点的值根据广度优先遍历。
*/
char[] valuesArray;
/**
* 完整二叉树中的索引
*/
int[] indicesArray;
/**
********************
* 将树转换为数据数组,包括 char 数组和 int 数组。完整二叉树中的索引。 他的结果储存在两个成员变量当中
*
* @see #valuesArray 这个放char
* @see #indicesArray 这个放int
*********************
*/
public void toDataArrays() {
// Initialize arrays.
int tempLength = getNumNodes();
valuesArray = new char[tempLength];
indicesArray = new int[tempLength];
int i = 0;
// Traverse and convert at the same time.
CircleObjectQueue tempQueue = new CircleObjectQueue();
tempQueue.enqueue(this);
CircleIntQueue tempIntQueue = new CircleIntQueue();
tempIntQueue.enqueue(0);
BinaryCharTree1 tempTree = (BinaryCharTree1) tempQueue.dequeue();
int tempIndex = tempIntQueue.dequeue();
while (tempTree != null) {
valuesArray[i] = tempTree.value;
indicesArray[i] = tempIndex;
i++;
if (tempTree.leftChild != null) {
tempQueue.enqueue(tempTree.leftChild);
tempIntQueue.enqueue(tempIndex * 2 + 1);
} // Of if
if (tempTree.rightChild != null) {
tempQueue.enqueue(tempTree.rightChild);
tempIntQueue.enqueue(tempIndex * 2 + 2);
} // Of if
tempTree = (BinaryCharTree1) tempQueue.dequeue();
tempIndex = tempIntQueue.dequeue();
} // Of while
}// Of toDataArrays
// 主函数入口
public static void main(String args[]) {
BinaryCharTree1 tempTree1 = manualConstructTree();
System.out.println("\r\nPreorder visit:");
tempTree1.preOrderVisit();
System.out.println("\r\nIn-order visit:");
tempTree1.inOrderVisit();
System.out.println("\r\nPost-order visit:");
tempTree1.postOrderVisit();
System.out.println("\r\n\r\nThe depth is: " + tempTree1.getDepth());
System.out.println("The number of nodes is: " + tempTree1.getNumNodes());
tempTree1.toDataArrays();
System.out.println("The values are: " + Arrays.toString(tempTree1.valuesArray));
System.out.println("The indices are: " + Arrays.toString(tempTree1.indicesArray));
}
}