为什么引用 (指针) 是无法存储到文件里面呢?
因为文件都是存储在磁盘上的,也就是外存,但我们在执行程序的时候,是需要将程序对应的进程加载到内存中,并且进程在内存中的地址不是固定的,这表明我们不能保证保存在文件中的指针在下次运行时所指向的内容还是我们之前所指向的内容。
所以我们该怎么保存二叉树呢?
保存结点之间的逻辑关系,就像数组一样,虽然每次运行时内存地址不一样,但它们的逻辑关系是确定的,我们就可以根据元素之间的逻辑关系和运行时的数组首地址来保存和访问各个元素。
介绍两种特殊的二叉树:
满二叉树:树中的每层都含有最多的结点,即叶子节点都在二叉树的最下一层并且除了叶子结点外的所有结点的度都为2。
如果树高为h,则一共有
2
h
−
1
2^h-1
2h−1个结点。
如果按照层序编号:根结点开始从0为每个结点编号,自上而下,自左向右,则可以表示为:
完全二叉树:如果树高为h,除了第h层,其它各层(1~h-1)的结点数都达到最大个数,且第h层的结点都依次排列在该层的最左边的位置上。
对完全二叉树进行层序编号可表示为:
如果用一个顺序表来记录这些结点,可表示为:
因此,我们可以把一棵普通的二叉树补充为一棵完全二叉树,用’\0’来表示补充的结点,然后用数组来保存各个结点的值及其父子关系。
设父结点的层次序号为
i
i
i,则其左孩子的序号为
i
∗
2
+
1
i*2+1
i∗2+1,右孩子的序号为
2
∗
i
+
2
2*i+2
2∗i+2。
但在实际运用中,这样的效果并不好,因为补充的完全二叉树有很多空结点,再采用这种方式会造成很多空间浪费,得不偿失了。
那我们就按照补充为完全二叉树的思路,但只记录实际存在的结点,用另一个数组来记录层次序号,两个数组通过数组索引实现层次序号和二叉树结点一一对应。
那么我们该怎么实现呢?
完全二叉树的层次序号的特点:自上而下,自左而右的编号。
这也是二叉树的一次层次遍历。
层次遍历的特点:从根结点出发,获取根结点的值,然后获取根结点的
左
孩
子
\large\color{Red}左孩子
左孩子的值,再是根结点的
右
孩
子
\large\color{Green}右孩子
右孩子;然后是根结点的
左
孩
子
\large\color{Red}左孩子
左孩子的左孩子,根结点
左
孩
子
\large\color{Red}左孩子
左孩子的右孩子;根结点
右
孩
子
\large\color{Green}右孩子
右孩子的左孩子,根结点
右
孩
子
\large\color{Green}右孩子
右孩子的右孩子……这刚好和队列的特点一致,获取出队元素,如果当前处理的结点存在孩子结点则让其孩子结点入队(满足从左到右的特点,先判断左孩子,再判断右孩子),直到队列为空。
初始化队列:让根结点入队。
序号处理:当处理当前结点时,可以根据当前结点计算出其子结点的层次序号,然后将获得的值压入另一个队列中。
序号队列代码:
package day07;
public class CircleIntQueue {
/**
* The total space. One space can never be used.
*/
public static final int TOTAL_SPACE = 10;
/**
* The data.
*/
int[] data;
/**
* The index for calculation the head. The actual head is head % TOTAL_SPACE.
*/
int head;
/**
* The index for calculating the tail.
*/
int tail;
public CircleIntQueue() {
data = new int[TOTAL_SPACE];
head = 0;
tail = 0;
}// Of the first constructor
/**
* Enqueue.
*
* @param paraValue The value of the new node.
*/
public void enqueue(int 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 int dequeue() {
if (head == tail) {
System.out.println("No element in the queue");
return -1;
} // Of if
int resultValue = data[head % TOTAL_SPACE];
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 - 1; i++) {
resultString += data[i % TOTAL_SPACE] + ", ";
} // Of for i
resultString += data[(tail - 1) % TOTAL_SPACE];
return resultString;
}// Of toString
/**
* The entrance of the program.
*
* @param args Not used now.
*/
public static void main(String args[]) {
CircleIntQueue tempQueue = new CircleIntQueue();
System.out.println("Initialized, the list is: " + tempQueue.toString());
for (int i = 0; i < 5; i++) {
tempQueue.enqueue(i + 1);
} // Of for i
System.out.println("Enqueue, the queue is: " + tempQueue.toString());
int tempValue = tempQueue.dequeue();
System.out.println("Dequeue " + tempValue + ", the queue is: " + tempQueue.toString());
for (int i = 0; i < 6; i++) {
tempQueue.enqueue(i + 10);
System.out.println("Enqueue, the queue is: " + tempQueue.toString());
} // Of for i
for (int i = 0; i < 3; i++) {
tempValue = tempQueue.dequeue();
System.out.println("Dequeue " + tempValue + ", the queue is: " + tempQueue.toString());
} // Of for i
for (int i = 0; i < 6; i++) {
tempQueue.enqueue(i + 100);
System.out.println("Enqueue, the queue is: " + tempQueue.toString());
} // Of for i
}// Of main
}// Of class CircleIntQueue
结点队列代码:
package day10;
/**
* Circle Object queue.
*
* @author Zhong Xiyan 976078956@qq.com
*/
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
/**
*
*********************
* @Title: enqueue
* @Description: TODO(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;
/**
*
*********************
* @Title: dequeue
* @Description: TODO(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 % TOTAL_SPACE];
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 - 1; i++) {
resultString += data[i % TOTAL_SPACE] + ", ";
} // Of for i
resultString += data[(tail - 1) % TOTAL_SPACE];
return resultString;
}// Of toString
/**
*
*********************
* @Title: main
* @Description: TODO(The entrance of the program.)
*
* @param args Not used now.
*********************
*
*/
public static void main(String args[]) {
CircleObjectQueue tempQueue = new CircleObjectQueue();
}// Of main
}// Of class CircleObjectQueue
二叉树存储:
package day10;
import java.util.Arrays;
import day07.CircleIntQueue;
public class BinaryCharTree {
/**
* The value in char.
*/
char value;
/**
* The left child.
*/
BinaryCharTree leftChild;
/**
* The right child.
*/
BinaryCharTree rightChild;
/**
*
*********************
* The first constructor.
*
* @param paraName The value.
*********************
*
*/
public BinaryCharTree(char paraName) {
value = paraName;
leftChild = null;
rightChild = null;
}// Of the constructor
/**
*
*********************
* @Title: manualConstructTree
* @Description: TODO(Manually construct a tree. Only for testing.)
*
* @return A binary tree.
*********************
*/
public static BinaryCharTree manualConstructTree() {
// Step 1. Construct a tree with only one node.
BinaryCharTree resultTree = new BinaryCharTree('a');
// Step 2. Construct all nodes. The first node is the root.
BinaryCharTree tempTreeB = new BinaryCharTree('b');
BinaryCharTree tempTreeC = new BinaryCharTree('c');
BinaryCharTree tempTreeD = new BinaryCharTree('d');
BinaryCharTree tempTreeE = new BinaryCharTree('e');
BinaryCharTree tempTreeF = new BinaryCharTree('f');
BinaryCharTree tempTreeG = new BinaryCharTree('g');
// Step 3. Link all nodes.
resultTree.leftChild = tempTreeB;
resultTree.rightChild = tempTreeC;
tempTreeB.rightChild = tempTreeD;
tempTreeC.leftChild = tempTreeE;
tempTreeD.leftChild = tempTreeF;
tempTreeD.rightChild = tempTreeG;
return resultTree;
}// Of manualConstructTree
/**
*
*********************
* @Title: getNumNodes
* @Description: TODO(Get the number of nodes)
*
* @return The number of nodes.
*********************
*
*/
public int getNumNodes() {
// It is a leaf.
if (leftChild == null && rightChild == null) {
return 1;
} // Of if
int leftChildNodes = 0, rightChildNodes = 0;
// Get the number of nodes of the left child.
if (leftChild != null) {
leftChildNodes = leftChild.getNumNodes();
} // Of if
// Get the number of nodes of the right child.
if (rightChild != null) {
rightChildNodes = rightChild.getNumNodes();
} // Of if
// The total number of nodes.
return leftChildNodes + rightChildNodes + 1;
}// Of getNumNodes
/**
* The values of nodes according to breadth first traversal.
*/
char[] valuesArray;
/**
* The indices in the complete binary tree.
*/
int[] indicesArray;
/**
*
*********************
* @Title: toDataArrays
* @Description: TODO(Convert the tree to data arrays, including a char array
* and an int array. The results are stored in two member
* variables)
*
* @see #valuesArray
* @see #indicesArray
*
*********************
*
*/
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);
BinaryCharTree tempTree = (BinaryCharTree) 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 = (BinaryCharTree) tempQueue.dequeue();
tempIndex = tempIntQueue.dequeue();
} // Of while
}// Of toDataArrays
/**
*
*********************
* @Title: main
* @Description: TODO(The entrance of the program.)
*
* @param args Not used now.
*********************
*
*/
public static void main(String args[]) {
BinaryCharTree tempTree = manualConstructTree();
tempTree.toDataArrays();
System.out.println("The values are: " + Arrays.toString(tempTree.valuesArray));
System.out.println("The indices are: " + Arrays.toString(tempTree.indicesArray));
}// Of main
}// Of class BinaryCharTree
运行结果:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)