实现能够将二叉树中序遍历和后序遍历的结果来重建二叉树的算法

实现能够将二叉树中序遍历和后序遍历的结果来重建二叉树的算法


本文的树用静态链表表示

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;
	}
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值