一级目录
二级目录
三级目录
利用顺序表建立二叉树
在使用通用队列存储二叉树时,我们用到了
/**
* the values of the nodes.
*/
char[] valuesArray;
/**
* the indices in the complete binary tree.
*/
int[] indicesArray;
分别来存储二叉树节点和整数的值,使用存储对象的队列来提高代码的复用性。那我们能否利用利用这两个数组进行二叉树的创建?
我们之前提到了采用下标和数据相互对应的顺序表来模拟二叉树,这里的下标之间刚好就满足我们需要的2 * i + 1或者2 * i + 2的左右儿子关系。如下图所示:
我们可以发现:一个节点的孩子节点只能在它对应顺序表的后面找到,对于某一节点来说,它的孩子节点在其后,它孩子节点的孩子节点也在其后,但是我们可以根据
1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子
来判断它的直接相连的节点。
于是我们的思路可以得出:从树的第一个非根分支结点开始,逐层向下进行遍历,遍历过程中枚举他的全部长辈,直到枚举出父级,并进行连接。
/**
* ********************
* The second constructor. The parameters must be correct since no validity
* check is undertaken.
*
* @param paraDataArray The array for data.
* @param paraIndicesArray The array for indices.
* ********************
*/
public BinaryCharTree(char[] paraDataArray, int[] paraIndicesArray) {
// Step 1. Use a sequential list to store all nodes.
int tempNumNodes = paraDataArray.length;
BinaryCharTree[] tempAllNodes = new BinaryCharTree[tempNumNodes];
for (int i = 0; i < tempNumNodes; i++) {
tempAllNodes[i] = new BinaryCharTree(paraDataArray[i]);
} // Of for i
// Step 2. Link these nodes.
for (int i = 1; i < tempNumNodes; i++) {
for (int j = 0; j < i; j++) {
System.out.println("indices " + paraIndicesArray[j] + " vs. " + paraIndicesArra
if (paraIndicesArray[i] == paraIndicesArray[j] * 2 + 1) {
tempAllNodes[j].leftChild = tempAllNodes[i];
System.out.println("Linking " + j + " with " + i);
break;
} else if (paraIndicesArray[i] == paraIndicesArray[j] * 2 + 2) {
tempAllNodes[j].rightChild = tempAllNodes[i];
System.out.println("Linking " + j + " with " + i);
break;
} // Of if
} // Of for j
} // Of for i
//Step 3. The root is the first node.
value = tempAllNodes[0].value;
leftChild = tempAllNodes[0].leftChild;
rightChild = tempAllNodes[0].rightChild;
}// Of the second constructor
首先需要把链式树的结点存起来,一个节点的孩子节点在顺序表对应的位置必然在其后面,反过来说,一个节点的父节点在顺序表中的位置必然在其前面,于是可以得出双重循环。
for (int i = 1; i < tempNumNodes; i++) {
是针对每一个节点
for (int j = 0; j < i; j++) {
是paraIndicesArray[i]对应节点的父类节点(包括直接和间接),其次判断是否为直接父类,如果是,则相连接。
运行结果
indices 0 vs. 1
Linking 0 with 1
indices 0 vs. 2
Linking 0 with 2
indices 0 vs. 4
indices 1 vs. 4
Linking 1 with 3
indices 0 vs. 5
indices 1 vs. 5
indices 2 vs. 5
Linking 2 with 4
indices 0 vs. 12
indices 1 vs. 12
indices 2 vs. 12
indices 4 vs. 12
indices 5 vs. 12
Linking 4 with 5
中序遍历
递归实现时,是函数自己调用自己,一层层的嵌套下去,操作系统/虚拟机自动帮我们用 栈 来保存了每个调用的函数,现在我们需要自己模拟这样的调用过程。
递归的调用过程是这样的:
dfs(root.left)
dfs(root.left)
dfs(root.left)
为null返回
打印节点
dfs(root.right)
dfs(root.left)
dfs(root.left)
........
递归的调用过程是不断往左边走,当左边走不下去了,就打印节点,并转向右边,然后右边继续这个过程。
我们在迭代实现时,就可以用栈来模拟上面的调用过程。
我们不断地往左子树方向走,每走一次就将当前节点保存到栈中,如果当tempNode为空时,说明左边走到头了,从栈中弹出节点并保存,然后转向右边节点,继续上面整个过程。
前序遍历
首先我们想要打印根节点的数据,此时Stack里面的内容为空,所以我们优先将头结点加入Stack,然后打印。
之后我们应该先打印左子树,然后右子树。所以先加入Stack的就是右子树,然后左子树。
后序遍历
先序遍历是中左右,后序遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图:
/**
* postOrder visit with a stack.
*/
public void postOrderVisitWithStack() {
ObjectStack tempStack = new ObjectStack();
BinaryCharTree tempNode = this;
tempStack.push(tempNode);
while(!tempStack.isEmpty())
{
tempNode=(BinaryCharTree) tempStack.pop();
System.out.println(" "+tempNode.value+" ");
if(tempNode.leftChild!=null)
{
tempStack.push(tempNode.leftChild);
}
if(tempNode.rightChild!=null)
{
tempStack.push(tempNode.rightChild);
}
}
}//of postOrderVisitWithStack
在这里我们可以用一个数组来存放出栈的结果,最后反转顺序即可得到后序遍历结果。
/**
*********************
* Post-order visit with stack.
*********************
*/
public void postOrderVisitWithStack() {
ObjectStack tempStack = new ObjectStack();
BinaryCharTree tempNode = this;
ObjectStack tempOutputStack = new ObjectStack();
while (!tempStack.isEmpty() || tempNode != null) {
if (tempNode != null) {
//Store for output.
tempOutputStack.push(new Character(tempNode.value));
tempStack.push(tempNode);
tempNode = tempNode.rightChild;
} else {
tempNode = (BinaryCharTree) tempStack.pop();
tempNode = tempNode.leftChild;
} // Of if
} // Of while
//Now reverse output.
while (!tempOutputStack.isEmpty()) {
System.out.print("" + tempOutputStack.pop() + " ");
}//Of while
}// Of postOrderVisitWithStack
这里采用的是中右左的入栈方式,同时记录下结点对应的值,最后进行反转。
二.代码展示
/**
* inOrder visit with a stack.
*/
public void inOrderVisitWithStack() {
ObjectStack tempStack = new ObjectStack();
BinaryCharTree tempNode = this;
while (!tempStack.isEmpty() || tempNode != null) {
if (tempNode != null) {
tempStack.push(tempNode);
tempNode = tempNode.leftChild;
} else {
tempNode = (BinaryCharTree) tempStack.pop();
System.out.println(" " + tempNode.value + " ");
tempNode = tempNode.rightChild;
}//of if
}//of while
}//of inOrderVisitWithStack
/**
* preOrder visit with a stack.
*/
public void preOrderVisitWithStack() {
ObjectStack tempStack = new ObjectStack();
BinaryCharTree tempNode = this;
tempStack.push(tempNode);
while(!tempStack.isEmpty())
{
tempNode=(BinaryCharTree) tempStack.pop();
System.out.println(" "+tempNode.value+" ");
if(tempNode.rightChild!=null)
{
tempStack.push(tempNode.rightChild);
}
if(tempNode.leftChild!=null)
{
tempStack.push(tempNode.leftChild);
}
}
}//of preOrderVisitWithStack
/**
* postOrder visit with a stack.
*/
public void postOrderVisitWithStack() {
ObjectStack tempStack = new ObjectStack();
BinaryCharTree tempNode = this;
ObjectStack tempOutputStack = new ObjectStack();
while (!tempStack.isEmpty() || tempNode != null) {
if (tempNode != null) {
//Store for output.
tempOutputStack.push(new Character(tempNode.value));
tempStack.push(tempNode);
tempNode = tempNode.rightChild;
} else {
tempNode = (BinaryCharTree) tempStack.pop();
tempNode = tempNode.leftChild;
} // Of if
} // Of while
//Now reverse output.
while (!tempOutputStack.isEmpty()) {
System.out.print("" + tempOutputStack.pop() + " ");
}//Of while
}//of postOrderVisitWithStack
运行结果
Preorder visit:
A
B
D
C
E
F
In-order visit:
B
D
A
E
F
C
Post-order visit:
D
B
F
E
C
A
In-order visit with a stack:
B
D
A
E
F
C
pre-order visit with a stack:
A
B
D
C
E
F
post-order visit with a stack:
D B F E C A
总结
二叉树的遍历有递归和迭代两种,使用递归是使用系统自带的栈来进行操作,只需要把
if (leftChild != null) {
leftChild.inOrderVisit();
}//of if
System.out.println("" + value + "");
if (rightChild != null) {
rightChild.inOrderVisit();
}//of if
这几句代码调换顺序即可得到结果。在采用迭代方法的时候,我们需要自己模拟这样的调用过程。
在后序遍历的过程中,很巧妙的利用到了前序遍历的特点,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中。今天再回头看二叉树的遍历,又获得了不少新的认知,自己用迭代模拟栈的实现,以及前后序遍历的相关性都会在之后的学习中有着影响。