一个例子:
前序序列:abdgcefh
中序序列:dgbaechf
求:后序序列
分析:
根据前序序列可知,a肯定是根节点。再在中序序列中找到a,发现a左侧是dgb,右侧是echf,则dgb应该是a的左孩子,echf是a的右孩子。
接着只要递归的判断左右孩子的后序序列就可以了。比如dgb是左孩子的中序序列,则可以根据abdgcefh得到它的前序序列是bdg。右孩子的做法一样。
编程实现思路:
(1)在前序序列中找第一个元素,作为根节点root;
(2)寻找root在中序序列中的位置index,根据index将中序序列分为两部分(leftMidOrder、rightMidOrder),根据index将前序序列分为两部分(leftPreOrder、rightPreOrder);
(3)根据leftPreOrder和leftMidOrder得到root节点的左孩子的后序序列leftPostOrder;
(4)根据rightPreOrder和rightMidOrder得到root节点的右孩子的后序序列rightPostOrder;
(5)leftPostOrder+rightPostOrder+root就是就是后序序列。
代码:
package binarytree;
public class RestoreBinaryTree {
/**
* 根据前序和中序,得到后序
* @param preOrder
* @param midOrder
* @return
*/
public static String getPostByPreAndMid(String[] preOrder, String[] midOrder) throws NullPointerException, IllegalArgumentException{
if(preOrder==null || midOrder==null) throw new NullPointerException("传递的数组不能为null!");
if(preOrder.length != midOrder.length) throw new IllegalArgumentException("前序数组和中序数组的长度不一致!");
if(preOrder.length==0) return "";
if(preOrder.length == 1) return preOrder[0];
String postOrder = "";
String root = preOrder[0];//get root
int index = indexOf(midOrder, root);
if(index==-1) throw new IllegalArgumentException("传递的参数不对");
String[] leftPreOrder = new String[index];
System.arraycopy(preOrder, 1, leftPreOrder, 0, index);
String[] leftMidOrder = new String[index];
System.arraycopy(midOrder, 0, leftMidOrder, 0, index);
String leftPostOrder = getPostByPreAndMid(leftPreOrder, leftMidOrder);
int rightPartLen = preOrder.length-index-1;
String[] rightPreOrder = new String[rightPartLen];
System.arraycopy(preOrder, index+1, rightPreOrder, 0, rightPartLen);
String[] rightMidOrder = new String[rightPartLen];
System.arraycopy(midOrder, index+1, rightMidOrder, 0, rightPartLen);
String rightPostOrder = getPostByPreAndMid(rightPreOrder, rightMidOrder);
if(leftPostOrder != null && !leftPostOrder.equals("")){
if(leftPostOrder.endsWith(",")){//判断的目的是为了输出的时候,每个元素以","隔开
postOrder += leftPostOrder;
}else{
postOrder += leftPostOrder + ",";
}
}
if(rightPostOrder != null && !rightPostOrder.equals("")){
if(rightPostOrder.endsWith(",")){
postOrder += rightPostOrder;
}else{
postOrder += rightPostOrder + ",";
}
}
postOrder += root;
return postOrder;
}
/**
* 获得元素在数组的索引
* @param strArr
* @param key
* @return
*/
private static int indexOf(String[] strArr, String key){
int index = -1;
for(int i=0; i<strArr.length; i++){
if(strArr[i].equals(key)){
index = i;
break;
}
}
return index;
}
public static void main(String[] args) {
String[] preOrder = new String[]{"a", "b", "d", "g", "c", "e", "f", "h"};
String[] midOrder = new String[]{"d", "g", "b", "a", "e", "c", "h", "f"};
String postOrder = getPostByPreAndMid(preOrder, midOrder);
System.out.println(postOrder);
}
}
扩展:
根据二叉树的后序序列和中序序列得到前序序列的思路和根据前序、中序得到后序的思路是一样的,不赘述了。
另外能不能根据前序序列和后序序列得到中序序列呢?有可能得到的是一个唯一的中序序列,也有可能得到的是多个中序序列。为什么呢?
原因就是:如果一个父节点只有一个孩子节点,无论这个孩子是左孩子还是右孩子,对于前序序列和后序序列都没有影响,所以这个时候对于相同的前序和后序序列,中序序列的情况可能有多种。那下面的问题就是:什么时候只有一种可能?如果有多种中序序列的可能,怎么求所有可能的中序序列呢?
(1)什么时候只有一种可能?
上面分析中已经指出,有多种中序序列可能的原因是非叶子节点只有一个孩子,所以,如果二叉树中所有的非叶子节点都有两个孩子,则中序序列是唯一确定的。
(2)如果有多种可能,怎么求所有可能的序列呢?
有多种可能,那么一定有若干个非叶子节点只有一个孩子,只要变换孩子的位置,就能得到一个不一样的中序序列。所以算法思路如下:
仍然是递归的想问题:
(1)如果节点的左右孩子皆为空,则返回节点本身;
(2)如果左孩子不为空,返回左孩子所有可能的中序序列;
(3)如果右孩子不为空,返回右孩子所有可能的中序序列;
(4)如果左右孩子均不空,以该节点为根的中序序列的所有可能是:从左孩子可能的中序序列取一个+根节点+右孩子可能的中序序列中取一个;如果只有左孩子,则所有的中序序列是:从左孩子取一个+根节点,和根节点+左孩子的任意一个;只有右孩子的情况和只有左孩子的情况一致。