今天的代码联动性比较强,需要用到辅助队列(之前写的改一下就派上用场),目标是要存储一颗二叉树。根据老师的描述:
我们可以完全满二叉树的角度广度优先遍历的角度来考虑这个问题: 每个节点都有一个 name 及其在二叉树中的位置. 令根节点的位置为 0; 则第 2 层节点的位置依次为 1 至 2; 第 3 层节点的位置依次为 3 至 6. 以此类推.
把昨天那个例子所对应的二叉树画出来, 我们有两种方法:
空使用 0 来表示, 可以用一个向量来存储:
[a, b, c, 0, d, e, 0, 0, 0, f, g]
优点: 仅需要一个向量, 简单直接.
缺点: 对于实际的二叉树, 很多子树为空, 导致大量的 0 值.
应使用压缩存储方式, 即将节点的位置和值均存储. 可表示为两个向量:
[0, 1, 2, 4, 5, 9, 10]
[a, b, c, d, e, f, g]
首先创建一个object类型的队列一会要用,直接在之前的代码上改:
package com.day09;
/**
* 循环对象类型队列.
*/
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 % TOTAL_SPACE];
head++;
return resultValue;
}// Of dequeue
}// Of CircleObjectQueue
之后接着昨天的代码加点两个属性与一个存储的方法:
/**
* 节点值数组
*/
char[] valuesArray;
/**
* 与节点值对应的下标数组
*/
int[] indicesArray;
/**
* *******************
* 将二叉树表示成两个数组
* 结果存储到两个成员变量中
*
* @see #valuesArray
* @see #indicesArray
* ********************
*/
public void toDataArrays() {
//数组长度由昨天写的方法得到.
int tempLength = getNumNodes();
valuesArray = new char[tempLength];
indicesArray = new int[tempLength];
int i = 0;
//遍历的同时更新数组
CircleObjectQueue tempQueue = new CircleObjectQueue();
tempQueue.enqueue(this);
//CircleIntQueue tempIntQueue = new CircleIntQueue();
CircleObjectQueue tempIntQueue = new CircleObjectQueue();
tempIntQueue.enqueue((Integer)0);
BinaryCharTree tempTree= (BinaryCharTree) tempQueue.dequeue();
Integer tempIndex = (Integer) tempIntQueue.dequeue();
while (tempTree != null) {
valuesArray[i] = tempTree.value;
indicesArray[i] = tempIndex;
i++;
if (tempTree.leftChild != null) {
tempQueue.enqueue(tempTree.leftChild);
tempIntQueue.enqueue((Integer)tempIndex * 2 + 1);
} // Of if
if (tempTree.rightChild != null) {
tempQueue.enqueue(tempTree.rightChild);
tempIntQueue.enqueue((Integer)tempIndex * 2 + 2);
} // Of if
tempTree = (BinaryCharTree) tempQueue.dequeue();
tempIndex = (Integer) tempIntQueue.dequeue();
} // Of while
}// Of toDataArrays
/**
* ********************
* 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
运行结果:
总结:
- 二叉树的存储这块感觉以前没怎么接触过,我个人理解二叉树的存储就是如何表示出二叉树,或者当我们要通过输入创建二叉树时,要输入什么,才能让计算机知道我们输入的是二叉树,而不是一堆无意义的数字。很容易想到暴力的使用领接表,但是空间利用率太低,所以根据老师的代码从完全满二叉树本身的性质入手,如果是我就很难想到用两个数组来表示出二叉树。
- 今天还使用了object类型的队列,队列里面放二叉树的节点,当然也能放Integer类型的对象,所以我两个队列都是object类型。
- 今天用到了层序遍历,层寻遍历的效果刚好与满二叉树的编号契合。相比于递归,层序遍历就要清晰明了多了,没有弯弯绕绕,以前:这还要借用队列真麻烦,现在:真好用,真方便,清晰明了。