您的位置:首页 > Web前端

(剑指offer笔记)根据前序遍历和后序遍历重建二叉树

2014-05-26 22:30 411 查看
问题:输入某二叉树的前序遍历和中序遍历的结果,重建出该二叉树,假设输入的前序遍历和终须遍历的结果都不含有重复数字。最后按层遍历输出二叉树结果。

public static void main(String[] args) {
// TODO 自动生成的方法存根
Character[] preOrder = new Character[] { 'a','b','d','g','c','e','f','h' };
Character[] inOrder = { 'd','g','b','a','e','c','h','f'};
Entry<Character> root = construct(preOrder, inOrder);
System.out.println(toString(root));
}


规定二叉树结点不含父节点字段。
private static class Entry<E> {
Entry<E> left, right;
E element;

Entry() {
}

Entry(E element) {
this.element = element;
}
}


解答:

前序遍历(根-左-右)

[java] view
plaincopy





preOrder(t){

if(t非空){

访问t的根元素;

preOrder(t的左子树);

preOrder(t的右子树);

}

}

中序遍历(左-根-右)

[java] view
plaincopy





inOrder(t){

if(t非空){

inOrder(t的左子树);

访问t的根元素;

inOrder(t的右子树);

}

}

前序遍历结果数组最左边总是根节点,而中序遍历结果数组中间某位置是根节点,左子树在根节点左边,右子树在根节点右边。我们现在前序遍历结果数组中得到根节点的值,然后在中序遍历结果数组中查找它,找到后对左右子树用同样方法迭代发现其根节点、左子树、右子树,由此构建二叉树。



程序使用泛型和迭代:

构建二叉树的函数分为两层,一层为public方法,可供外界程序调用;一层为private方法,实现构建功能:
public static <E> Entry<E> construct(E[] preOrder, E[] inOrder) {
if (preOrder == null || inOrder == null
|| preOrder.length != inOrder.length || inOrder.length <= 0)
return null;//检查输入的数组是否有效
return coreConstruct(preOrder, 0, preOrder.length - 1, inOrder, 0,
preOrder.length - 1);
}


private static <E> Entry<E> coreConstruct(E[] preOrder, int startPreOrder,
int endPreOrder, E[] inOrder, int startInOrder, int endInOrder) {
Entry<E> root = new Entry<E>(preOrder[startPreOrder]);// 前序遍历数组的最左边的元素是二叉树的根节点
// 在中序遍历数组中找到该根节点元素,它左边是左子树,右边是右子树
int rootInOrder = search(inOrder, root.element);//找到根结点索引
if (rootInOrder == -1) {//如果没找到该结点元素,说明给定的数组有问题
throw new IllegalArgumentException("输入数组无效");
}
int leftLength = rootInOrder - startInOrder;//左子树长度
int leftEndPreOrder = startPreOrder + leftLength;//前序遍历结果数组里当前根结点的左子树的最后一个元素的位置。
// 对其左子树和右子树迭代重建
if (leftLength > 0) {
// 如果左子树长度大于0,那就对左子树进行重建
root.left = coreConstruct(preOrder, startPreOrder + 1,
leftEndPreOrder, inOrder, startInOrder, rootInOrder - 1);
}
if (leftEndPreOrder < endPreOrder) {
//如果左子树最后一个元素的索引位置小于整个前序遍历数组最后一个索引位置,说明有右子树存在, 重建右子树
root.right = coreConstruct(preOrder, leftEndPreOrder + 1,
endPreOrder, inOrder, rootInOrder + 1, endInOrder);
}
return root;
}


/**
* @param array 待搜索的数组
* @param e 要被搜索的元素
* @return 被搜索的元素在数组中的位置。如果没有找到,就返回-1
*/
public static <E> int search(E[] array, E e) {
for (int i = 0; i < array.length; i++) {
if (array[i].equals(e))
return i;
}
return -1;
}


重建完毕后需要按层遍历输出

逐层遍历(宽度优先遍历)

[java] view
plaincopy





breadthFirst(t){

//queue为二叉树(引用)的队列

//t为二叉树(引用)

if(t非空){

queue.enqueue(t);

while(queue不空){

queue.dequeue(t); //从队列中移除根结点

访问被移除的根;

if(tree的左子树非空)

queue.enqueue(tree的左子树);

if(tree的右子树非空)

queue.enqueue(tree的右子树);

}

}

}

核心思想是利用队列的先进先出特点,根结点先进去,叶子节点后进去。用一个队列保存一层的结点,当访问该层某结点的子节点时,就被该结点移除出队列,访问它,然后在队列末尾插入子节点。在while循环中不断重复该过程。于是所有节点就按照从根开始一层一层地从左到右地访问完毕。
/**
* 宽度遍历,用一个链表保存结果
*
* @param root
* @return
*/
public static <E> List<E> breadthFirst(Entry<E> root) {
LinkedList<Entry<E>> list = new LinkedList<Entry<E>>();
ArrayList<E> result = new ArrayList<E>();
if (root != null)
list.addLast(root);
while (!list.isEmpty()) {
Entry<E> p = list.removeFirst();
result.add(p.element);
if (p.left != null) {
list.addLast(p.left);
}
if (p.right != null) {
list.addLast(p.right);
}
}
return result;
}


/**
* 使用宽度遍历逐层打印元素
*
* @param root
*            根节点
* @return
*/
public static <E> String toString(Entry<E> root) {
return breadthFirst(root).toString();
}


输出结果为[a, b, c, d, e, f, g, h]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐