#数据结构与算法学习笔记#PTA11:先序遍历+中序遍历转后序遍历/二叉树非递归遍历/二叉树栈遍历(JAVA)

2018.4.18

这道题说的是利用栈来模拟二叉树,通过栈的出入情况,模拟出这棵二叉树,并后序遍历打印这棵二叉树。

这题关键点在于理解二叉树的非递归遍历,能够看得出入栈顺序就是二叉树的先序遍历序列,出栈顺序就是二叉树的中序遍历序列。理解了这点这道题就转换成已知先序遍历+中序遍历,打印二叉树的后序遍历的问题了。

有两个思路:1.根据先序遍历和中序遍历先构建一棵二叉树,再后序遍历打印这棵树;2.直接通过先序遍历和中序遍历得出后序遍历序列。其实能够做出思路1,想想肯定能做出思路2。本题我采用的是思路2。

思路概述:先序遍历的第一个结点,在中序遍历中的位置i,i对应根节点,中序遍历中i左半部分的元素即为根节点左子树,右半部分元素即为根节点右子树。通过先序遍历先递归左子树的根节点,再递归右子树的根节点,直到递归到叶节点时打印,在递归函数出栈时依次打印根节点,就可以输出该二叉树的后序遍历。

这题其实很简单,但是我碰上了两个大坑,1.在验证输入字符串时,不能用==,要用equals;2.注意N大于10时字符串为2位数,不能用int data = str[1].charAt(0) - '0';,要用int data = Integer.parseInt(str[1]);,否则N>10输出结果不正确。为了查出第二个大坑花了两个晚上,之前因为十位以内的输入都正确了,输入push等又太麻烦,因此调试的时候都直接跳过了输入步骤直接给前序遍历和中序遍历赋值查输出结果。查到错之后感慨万分,debug这种事还是得踏踏实实地来啊。(代码中和文后附一个自己手撕的N=30的测试样例)

 

An inorder binary tree traversal can be implemented in a non-recursive way with a stack. For example, suppose that when a 6-node binary tree (with the keys numbered from 1 to 6) is traversed, the stack operations are: push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6); pop(); pop(). Then a unique binary tree (shown in Figure) can be generated from this sequence of operations. Your task is to give the postorder traversal sequence of this tree.

#数据结构与算法学习笔记#PTA11:先序遍历+中序遍历转后序遍历/二叉树非递归遍历/二叉树栈遍历(JAVA)_第1张图片


Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤30) which is the total number of nodes in a tree (and hence the nodes are numbered from 1 to N). Then 2N lines follow, each describes a stack operation in the format: "Push X" where X is the index of the node being pushed onto the stack; or "Pop" meaning to pop one node from the stack.

Output Specification:

 

For each test case, print the postorder traversal sequence of the corresponding tree in one line. A solution is guaranteed to exist. All the numbers must be separated by exactly one space, and there must be no extra space at the end of the line.

 

#数据结构与算法学习笔记#PTA11:先序遍历+中序遍历转后序遍历/二叉树非递归遍历/二叉树栈遍历(JAVA)_第2张图片

以下是具体实现(被注释的内容均为当初debug留下的印记,大坑啊):

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;

public class TreeTraversalsAgain {

	private static ArrayList inorderTree = new ArrayList(); // 出栈顺序,即中序遍历顺序
	private static ArrayList inputList = new ArrayList(); // 入栈顺序,即先序遍历顺序
	private static ArrayList postorderTree = new ArrayList(); // 后序遍历顺序
	private static int num = 0;
	private static Scanner indata = new Scanner(System.in);
	
	//30测试用例
	private static int[] pre = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29};
	private static int[] in = {3,5,4,8,7,6,2,1,10,9,12,14,13,15,11,0,17,19,18,21,23,22,20,16,24,25,26,28,29,27};

	private static void Init() {

		Stack stack = new Stack();
		num = Integer.parseInt(indata.nextLine());
		
		for (int i = 0; i < 2 * num; i++) {
			String[] str = indata.nextLine().split(" ");
			if (str[0].equals("Push")) { // 这里不能用==,要用equals
				int data = Integer.parseInt(str[1]);//这里不能用str[1].charAt(0) - '0',因为N大于10时字符串为2位数
				inputList.add(data);
				stack.push(data);
			} else if (str[0].equals("Pop")) {
				inorderTree.add(stack.pop());
			}
		}
		/*
		//测试用例
		num = 30;
		for(int i=0;i<30;i++){
			inputList.add(pre[i]);
			inorderTree.add(in[i]);
		}
		*/
	}

	private static void PostorderTree() {
		int flag = 0; // input元素下标
		int head = 0;
		int tail = num - 1;
		int root = Find(inputList.get(flag)); // 根节点的下标

		if(root != -1){
			Print(root, head, tail, flag);
		}

	}

	// 返回指定元素的下标
	private static int Find(int data) {
		for (int i = 0; i < num; i++) {
			if (inorderTree.get(i) == data) {
				return i;
			}
		}
		return -1;
	}

	private static void Print(int root, int head, int tail, int flag) {
		int temp = flag + (root - head + 1);	//存在右子树情况下的前序遍历root对应下标
		// 左右子树均有,递归先打印左子树,后递归打右子树
		if (root != head && root != tail) {			
			Print(Find(inputList.get(flag + 1)), head, root - 1, flag + 1);
			Print(Find(inputList.get(temp)), root + 1, tail, temp);
		}
		// 只有右子树,递归打右子树
		else if (root == head && tail != root) {
			Print(Find(inputList.get(temp)), root + 1, tail, temp);
		}
		// 只有左子树,递归打左子树
		else if (root == tail && head != root) {
			Print(Find(inputList.get(flag + 1)), head, root - 1, flag + 1);
		}
		// 递归终点打印叶子结点,递归函数出栈过程依次打印各个根结点
		postorderTree.add(inorderTree.get(root));
	}
	
	
	private static void Out() {
		System.out.print("post:");
		if (!postorderTree.isEmpty()) {
			System.out.print(postorderTree.get(0));
		}
		for (int i = 1; i < postorderTree.size(); i++) {
			System.out.print(" " + postorderTree.get(i));
		}
	}
	
	/*
	//打印先序遍历
	private static void Out2() {
		System.out.print("pre:");
		if (!inputList.isEmpty()) {
			System.out.print(inputList.get(0));
		}
		for (int i = 1; i < inputList.size(); i++) {
			System.out.print(" " + inputList.get(i));
		}
		System.out.println("");
	}
	
	//打印中序遍历
	private static void Out3() {
		System.out.print("in:");
		if (!inorderTree.isEmpty()) {
			System.out.print(inorderTree.get(0));
		}
		for (int i = 1; i < inorderTree.size(); i++) {
			System.out.print(" " + inorderTree.get(i));
		}
		System.out.println("");
	}
	*/

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Init();
		//Out2();
		//Out3();
		PostorderTree();
		Out();
		indata.close();
	}

}

附一颗N=30的复杂二叉树测试用例,你们在测试自己代码时可能可以用上:

#数据结构与算法学习笔记#PTA11:先序遍历+中序遍历转后序遍历/二叉树非递归遍历/二叉树栈遍历(JAVA)_第3张图片

Push 0
Push 1
Push 2
Push 3
Pop
Push 4
Push 5
Pop
Pop
Push 6
Push 7
Push 8
Pop
Pop
Pop
Pop
Pop
Push 9
Push 10
Pop
Pop
Push 11
Push 12
Pop
Push 13
Push 14
Pop
Pop
Push 15
Pop
Pop
Pop
Push 16
Push 17
Pop
Push 18
Push 19
Pop
Pop
Push 20
Push 21
Pop
Push 22
Push 23
Pop
Pop
Pop
Pop
Push 24
Pop
Push 25
Pop
Push 26
Pop
Push 27
Push 28
Pop
Push 29
Pop
Pop

#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

 

你可能感兴趣的:(JAVA,PTA,数据结构与算法)