二叉树的存储
一、顺序存储结构
二叉树的顺序存储是指用一组地址连续的存储单元依次自上而下,自左至右存储完全二叉树上的节点元素,老师的代码中使用的是前面使用过的循环列表对二叉树进行存储。
对于一般的二叉树(如代码中构建的二叉树),为了让数组下标能反映二叉树中结点之间的逻辑关系,只能添加一些并不存在的空节点,让其每个节点与完全二叉树上的结点相对照,再存储到一维数组的对应分量中。昨天的二叉树如下图:
用一个向量来存储:
[a, b, c, 0, d, e, 0, 0, 0, f, g]
优点:仅需要一个向量,简单粗暴
缺点:对于实际的二叉树,很多子树为空,导致大量的0值。
在最坏的情况下,一个高度为h且只有h个节点的单支树却需要占据近
2
h
−
1
{2^h -1}
2h−1个存储空间。
使用顺序存储结构存储二叉树还需注意父节点与子节点在同一向量中,对应序号的数学关系:
n
L
e
f
t
C
h
i
l
d
=
2
∗
n
R
o
o
t
+
1
{n_{LeftChild} = 2 * n_{Root} + 1}
nLeftChild=2∗nRoot+1
n
R
i
g
h
t
C
h
i
l
d
=
2
∗
n
R
o
o
t
+
2
{n_{RightChild} = 2 * n_{Root} + 2}
nRightChild=2∗nRoot+2
二、BFS遍历
这段代码中,遍历的过程即是在循环队列中插入二叉树节点的过程。代码如下:
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
在查阅BFS遍历资料时,发现层序遍历这个遍历算法,对二叉树的遍历,层序遍历可以返回一个二维数组,而BFS的遍历结果只是一个一维数组。
三、代码与数据测试
今天的代码是在昨天的二叉树代码上加写的。
package datastructure.tree;
import java.util.Arrays;
import datastructure.queue.*;
/**
* Binary tree with char type elements.
*
* @auther Weijie Pu weijiepu@163.com.
*/
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
/**
********************
* Mannually construct a tree. Only for testing.
********************
*/
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.
// BinaryCharTreeNode tempTreeA = resultTree.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 maualConstructTree
/**
********************
* Pre-order visit.
********************
*/
public void preOrderVisit() {
System.out.print("" + value + " ");
if (leftChild != null) {
leftChild.preOrderVisit();
} // Of if
if (rightChild != null) {
rightChild.preOrderVisit();
} // Of if
} // Of preOrderVisit
/**
********************
* In-order visit.
********************
*/
public void inOrderVisit() {
if (leftChild != null) {
leftChild.inOrderVisit();
} // Of if
System.out.print("" + value + " ");
if (rightChild != null) {
rightChild.inOrderVisit();
} // Of if
} // Of inOrderVisit
/**
********************
* Post-order visit.
********************
*/
public void postOrderVisit() {
if (leftChild != null) {
leftChild.postOrderVisit();
} // Of if
if (rightChild != null) {
rightChild.postOrderVisit();
} // Of if
System.out.print("" + value + " ");
} // Of postOrderVisit
/**
********************
* Get the depth of the binary tree.
*
* @return The depth. It is 1 if there is only one node. i.e., the root.
********************
*/
public int getDepth() {
// It is a leaf.
if ((leftChild == null) && (rightChild == null)) {
return 1;
} // Of if
// The depth of the left child.
int tempLeftDepth = 0;
if (leftChild != null) {
tempLeftDepth = leftChild.getDepth();
} // Of if
// The depth of the right child.
int tempRightDepth = 0;
if (rightChild != null) {
tempRightDepth = rightChild.getDepth();
} // Of if
// The depth should increment by 1.
if (tempLeftDepth >= tempRightDepth) {
return tempLeftDepth + 1;
} else {
return tempRightDepth + 1;
} // Of if
} // Of getDepth
/**
********************
* 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
// The number of nodes of the left child.
int tempLeftNodes = 0;
if (leftChild != null) {
tempLeftNodes = leftChild.getNumNodes();
} // Of if
// The number of nodes of the right child.
int tempRightNodes = 0;
if (rightChild != null) {
tempRightNodes = rightChild.getNumNodes();
} // Of if
// The total number of nodes.
return tempLeftNodes + tempRightNodes + 1;
}
/**
* The values of nodes according to breadth first traversal.
*/
char[] valuesArray;
/**
* The indices in the complete binary tree.
*/
int[] indicesArray;
/**
********************
* 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
/**
********************
* The entrance of the program.
*
* @param args
* Not used now.
********************
*/
public static void main(String args[]) {
BinaryCharTree tempTree = manualConstructTree();
System.out.println("\r\nPreorder visit:");
tempTree.preOrderVisit();
System.out.println("\r\nIn-order visit:");
tempTree.inOrderVisit();
System.out.println("\r\nPost-order visit:");
tempTree.postOrderVisit();
System.out.println("\r\n\r\nThe depth is: " + tempTree.getDepth());
System.out.println("The number of nodes is: " + tempTree.getNumNodes());
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 BinaryCharTree
运行结果: