PAT1020二叉树遍历

1020 Tree Traversals (25分)

Suppose that all the keys in a binary tree are distinct positive integers. Given the postorder and inorder traversal sequences, you are supposed to output the level order traversal sequence of the corresponding binary tree.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (≤30), the total number of nodes in the binary tree. The second line gives the postorder sequence and the third line gives the inorder sequence. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print in one line the level order traversal sequence of the corresponding binary tree. All the numbers in a line must be separated by exactly one space, and there must be no extra space at the end of the line.

Sample Input:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

Sample Output:

4 1 6 3 5 7 2

题目大意:

给定一棵二叉树的后序遍历和中序遍历,让你求出这棵二叉树的层序遍历

思路分析:

最简单的思路是,用后序遍历和中序遍历,重建这棵二叉树,然后我们用一个队列去遍历这棵二叉树,从而得到最后的层序遍历结果。这里有两个关键的地方,一个是二叉树的重建,一个是二叉树的层序遍历。首先我们看给出中序和后序如何重建二叉树。我们知道后序的最后一个节点就是当前这棵树的根节点。那么我们在中序中找到这个节点,就可以把这棵树分为 根节点、左子树、右子树。

2 3 1 5 7 6 4   后序
1 2 3 4 5 6 7   中序
index0123456
value(后序)2315764
value(中序)1234567

我们以这棵树为例,首先对于整棵树 后序数组中的左边界postLeft = 0 ,后序数组的右边界为 postRight = 6 ,中序数组的左边界 inLeft=0 中序数组的右边界为 inRight = 6 .此时,后序的最后一个数据就是当前这棵树的根节点,在这里就是postIndex = 6 这个位置,数据为4 。拿到这个数据后,我们在中序数组中进行遍历搜索 4 这个数据,最后会找到inIndex = 3 这个位置。于是我们可以把整棵树划分为 根节点 ,左子树,右子树。

对中序数组来说,根节点的位置为 rootIndex = 3 那么左子树范围为【InLeft …InRootIndex -1】 右子树范围为【inRootIndex+1 …InRight】 ,这里的下标范围都是在各自的遍历数组中进行取值。不能跑到另外的数组中取。

对于后序数组来说,我们求解左右子树的下标范围需要借助前面的中序。由于中序数组的根节点下标恰好能把左右子树节点分开,因此比较好求解左右子树的节点下标范围。但是后序的左右下标是连在一起的。因此我们需要知道左右的子树的各自的节点数量。那么由前面左子树的节点区间范围可以求得当前左子树节点数量为leftLength = inRootIndex-1 - inLeft + 1 = (inRootIndex - inLeft) . 右子树的节点数量为rightLength = inRight-(inRootIndex+1)+1 = (inRight - inRootIndex) 。于是我们就可以求得在后序数组中,左子树范围【postLeft …postLeft+leftLength-1】 右子树的范围是【postLeft+leftLength …postRight-1】.其实我们知道左子树节点的数量,就不用求解右子树的数量再去算出后序右子树范围。这是因为,一旦求出在后序中,左子树的范围,那么,左子树的右边界+1 …根节点-1 这个范围自然就是后序中的右子树范围。

有了在第一棵树中划分出左右子树的经验,我们就可以递归地 在左右子树中做同样的事情。这样这棵树就可以递归地创建起来了。

完整代码:

import java.util.*;

public class P1020 {
   static class Node {  // 构建节点子类
      int val;
      Node left;
      Node right;

      public Node(int val, Node left, Node right) {
         this.val = val;
         this.left = left;
         this.right = right;
      }

   }

   public static void main(String[] args) {
      Scanner scanner = new Scanner(System.in);
      int num = scanner.nextInt();
      int[] post = new int[num];
      int[] inord = new int[num];
      for (int i = 0; i < num; i++)
         post[i] = scanner.nextInt();
      for (int j = 0; j < num; j++)
         inord[j] = scanner.nextInt();
	
      Node root = CreatTree(post, inord, 0, num - 1, 0, num - 1);// 创建二叉树

      Queue<Node> queue = new LinkedList<>();
      List<Integer> list = new ArrayList<>();
      queue.offer(root);
	// 层序遍历,首先根节点入队,当队列不为空时,进行循环。  弹出队首元素,加入层序表,如果队首				元素的左孩子不为  空,那么就把它加入队列,如果队首元素的右孩子不为空,也把它加入队列。直到最后队列为空,退出循环。
      while (queue.size() != 0) {
         Node node = queue.poll();
         list.add(node.val);
         if (node.left != null)
            queue.offer(node.left);
         if (node.right != null)
            queue.offer(node.right);
      }
		// 依次输出list 列表即可
      for (int i = 0; i < list.size(); i++) {
         System.out.print(list.get(i));
         if (i != list.size() - 1)
            System.out.print(' ');

      }


   }
	// 由后序和中序构建二叉树 我们需要标记每一轮的左右子树的区间端点。因为方法需要四个index
   static Node CreatTree(int[] post, int[] inord, int Pstart, int Pend, int Istart, int Iend) {
      if (Pstart  > Pend) {  // 如果任何一个区间的左端点>右端点,说明整个区间已经没有值了。直接返回null
         return null ;
      }

      int rootData = post[Pend]; // 找到后序的根节点值
      int index = -1;
      for (int i = Istart; i <= Iend; i++) { // 在中序中找到这个根节点的下标。
         if (inord[i] == rootData) {
            index = i;
            break;
         }
      }
      Node root = new Node(rootData, null, null); // 建立根节点
      int leftInStart = Istart;     // 中序数组中:左子树的左端点
      int leftInend = index - 1;   // 中序数组中:左子树的右端点
      int rightStart = index + 1; // 中序数组中:右子树的左端点
      int rightend = Iend;       // 中序数组中:右子树的左端点

      int leftPstart = Pstart;  // 后序数组中:左子树的左端点
      int leftPend = leftPstart - 1 + (leftInend - leftInStart + 1);// 后序数组中:左子树的右端点
      int rightPsart = leftPend + 1; // 后序数组中:右子树的左端点
      int rightPend = Pend - 1;   // 后序数组中:右子树的右端点
	   // 然后分别递归创建左右子树  最后返回当前子树的根节点root 和上一层节点挂接起来。
      root.left = CreatTree(post, inord, leftPstart, leftPend, leftInStart, leftInend);
      root.right = CreatTree(post, inord, rightPsart, rightPend, rightStart, rightend);
      return root;
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值