实现能够将二叉树中序遍历和后序遍历的结果来重建二叉树的算法
文章目录
本文的树用静态链表表示
1.算法分析过程
先讨论简单的情况,假设二叉树结构如下:
它的中序遍历结果是:{A C B}
它的后序遍历结果是:{A B C}
假设只有中序和后序遍历结果,应当如何还原该树?事实上,从后序遍历的结果可以确定整棵树的根为C。此时再看中序遍历的结果,在C前面先输出的为C的左子树部分吗,在B后面输出的为C的右子树部分。因为此时C的左右子树部分都只有一个节点,因此我们可以确定树根为C,其左子树为A,右子树为B。
从而,对于任意的中序与后序遍历结果,我们都可以得到结论如下:
可以通过后序遍历结果确定树根,再从中序遍历结果确定根的左右子树部分。
它的中序遍历结果:{根的左子树部分、根、根的右子树部分}
它的后序遍历结果:{根的左右子树部分、根}
2.算法实现
在本实现中,使用静态链表来实现树。
然后确定递归方法的宏观语义
此时,方法体内的框架应当为:
// 递归终止条件
// 确定根
// 确定根的左子树
// 确定根的右子树
接下来分四个部分讲述整个方法的实现。
(1)递归终止条件
当树空,即中序/后序遍历的结果为空时,直接返回-1即可。
(2)确定根
后序遍历结果的最后一个即是树根,然后将此树根插入到树中即可。
(3)确定根的左子树
对于根的左子树,本文递归的调用function即可重建根的左子树并得到根的左子树所在的位置。然后,将根与左子树相连接即可完成确定根左子树的操作。
在本部分中,主要的任务在于给出左子树部分的中序与后序遍历结果。这样才能进一步调用function。
(4)确定根的右子树(类似于上一步)
对于根的右子树,本文递归的调用function即可重建根的右子树并得到根的右子树所在的位置。然后,将根与右子树相连接即可完成确定根右子树的操作。
在本部分中,主要的任务在于给出右子树部分的中序与后序遍历结果。这样才能进一步调用function。
3.演示
在测试中使用的树如下。
本文给定中序遍历结果{ ‘B’, ‘D’, ‘F’, ‘A’, ‘C’, ‘E’ }、后序遍历结果{ ‘F’, ‘D’, ‘B’, ‘E’, ‘C’, ‘A’ }。
程序输出结果如下:
下面对输出结果做出简单解释。以第一行为例,A节点为0号节点,其左孩子为1号节点,右孩子为4号节点。
再以第二行为例,B节点左孩子为-1号节点即不存在左孩子,右孩子为2号节点。
如此,我们已经得到了树的完整结构,完成了二叉树的重建。
4.代码
package Lab8.Task2;
public class Task2 {
static char[] inOrder = { 'B', 'D', 'F', 'A', 'C', 'E' };
static char[] postOrder = { 'F', 'D', 'B', 'E', 'C', 'A' };
static Node[] tree; /* 表示一颗树 */
// 树节点的实现
static class Node {
int index;/* 在tree中的位置 */
char ch;/* 节点的关键字 */
int leftChild;/* 左孩子在tree中的位置 */
int rightChild;/* 右孩子在tree中的位置 */
Node(int index, char ch, int leftChild, int rightChild) {
this.index = index;
this.ch = ch;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
tree = new Node[inOrder.length];
function(inOrder, postOrder);
for (int i = 0; i < tree.length; i++) {
System.out.println("节点" + tree[i].index + ":" + " 节点字符:" + tree[i].ch + " 左孩子:" + tree[i].leftChild
+ " 右孩子:" + tree[i].rightChild);
}
}
/* 根据给定的中序与后序遍历结果,得到tree并返回整棵树root的位置 */
private static int function(char[] inOrder, char[] postOrder) {
int rootIndex = -1;
if (inOrder.length == 0)
return rootIndex;
char[] newInOrder;
char[] newPostOrder;
// 确定当前根======================================
char rootVal = postOrder[postOrder.length - 1];/* 后续遍历结果最后一个为根 */
for (int i = 0; i < tree.length; i++) {/* 确定节点在tree中的插入index */
if (tree[i] == null) {
rootIndex = i;
break;
}
}
Node root = new Node(rootIndex, rootVal, -1, -1);
tree[rootIndex] = root;/* 把该节点添加进tree中 */
// 确定左子树=======================================
int len = 0;
/* 确定newInOrder长度 */
for (int i = 0; i < inOrder.length; i++) {
if (inOrder[i] == rootVal) {
len = i;
break;
}
}
/* 填充newInOrder */
newInOrder = new char[len];
for (int i = 0; i < newInOrder.length; i++) {
newInOrder[i] = inOrder[i];
}
/* 确定newPostOrder长度 */
/* 填充newPostOrder */
newPostOrder = new char[len];
for (int i = 0; i < newPostOrder.length; i++) {
newPostOrder[i] = postOrder[i];
}
root.leftChild = function(newInOrder, newPostOrder);/* 递归确定左子树 */
// ================================================
// 确定右子树
/* 确定newInOrder长度 */
int index = len; /* 根在inOrder中的索引 */
len = inOrder.length - index - 1;
/* 填充newInOrder */
int index2 = 0;
newInOrder = new char[len];
for (int i = index + 1; i < inOrder.length; i++) {
newInOrder[index2++] = inOrder[i];
}
/* 确定newPostOrder长度 */
/* 填充newPostOrder */
index2 = 0;
newPostOrder = new char[len];
for (int i = postOrder.length - 1 - len; i < postOrder.length - 1; i++) {
newPostOrder[index2++] = postOrder[i];
}
root.rightChild = function(newInOrder, newPostOrder);/* 递归确定右子树 */
// =================================================
return rootIndex;
}
}