import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Queue;
 import java.util.Stack;//二叉树的基本算法-递归序
 public class Binary {//类中类,定义数据类型
public static class Node{
	public int value;
	public Node left;
	public Node right;
	//构造器,将调用输入作为value变量
	public Node(int v) {
		 value = v;
	}
	
	/*                // 递归序 方式 实现二叉树的遍历(先序、中序、后序)
	public static void Binary(Node head) {
		if(head != null)return;
		// System.out.println(head.value + "\t");       //放这里就是 先序
		Binary(head.left);
		// System.out.println(head.value + "\t");       //放这里就是 中序
		Binary(head.right);
		// System.out.println(head.value + "\t");       //放这里就是 后序
	} */
	
	                // 非递归的方式 实现二叉树的遍历(先序、中序、后序)
	//先序---常见方法
	public static void pre(Node head) {
		//当堆顶为空,即没有元素-直接退出
		if(head == null) return;
		System.out.print("Pre-order");  //友好提示:先序		
		
		//创建一个堆-用来存放先序的输出
		Stack<Node> stack = new Stack<Node>();
		//放入头节点head在堆stack中
		stack.add(head);
		
		//当堆里元素不为空时
		while(!stack.isEmpty()) {
			stack.pop();     //打印每次的堆元素 和 对应的值
			System.out.print(head.value+"\t");
			//由于堆-先进后出、先放right右节点进,left左节点先出
			if(head.right != null) {
				stack.push(head.right);      //push方法-即判断栈是否还有空间-有就进行添加并进行结构化
			}
			if(head.left != null) {
				stack.push(head.left);
			}
		}
		System.out.println();
	}
	
	
	                         //第二种方法-后序
	public static void pos(Node h) {        // h 用来记录每次要打印的位置(即每次打印前在栈顶的值)
		System.out.println("Pos-order");   //友好提示:后序
		if(h != null ) {
			Stack<Node> stack = new Stack<Node>();     //新建栈
			stack.push(h);            //push方法-即判断栈是否还有空间-有就进行添加到栈里
			/*
			 * stack:add与push:都是将元素放入栈中,不过返回值类型不同(add:boolea ,push:参数类型)-add是继承Vector的,push是stack自身的
			 * stack:pop与peek:都会返回栈顶值,不过(pop:弹出值-即在栈中删除值,peek:不弹出值)
			 */
			
			Node c = null;            // c 按规律依此遍历数组---先打印最低子左节点的左右节点,直到左边节点全部打印完成,同样再打印右边(子树的子左、子右、头)
			while(!stack.isEmpty()) {    //栈不等于空时
				c = stack.peek();      // c 再指向当前子树的peek-顶部;
				//当头节点c的子左节点不为空、h不指向c的子左右节点时
				//------开始会直接找到最低层的左节点,然后直接弹出打印,再在打印最底层子右节点、再将他们头节点的头节点作为最低层子左节点(不断循环)
				if(c.left != null && h!=c.left && h!=c.right) {
					//将c.left 放入栈中
					stack.push(c.left);
				}//当头节点c的子右节点不为空、h不指向c的子右节点时
				if(c.right != null && h!=c.right) {
					stack.push(c.right);
				}else {    
					//其他情况:弹出当前的栈顶值, 并让 h 指向 c 的位置
					System.out.println(stack.pop().value +"\t");
					h=c;
				}
			}
		}
		System.out.println();
	}
	
	
	
	                                       //以层次的方式遍历堆,并且输出-普通
	public static void pre1(Node head) {
		if(head == null) return; 
		
		//创建一个列表:
		Queue<Node> queue = new LinkedList<>();
		//将头节点head元素放入列表中
		queue.add(head);
		
		while(!queue.isEmpty()) {
			//列表打印的元素堆位置变量cur,         --- 打印一个堆位置,就添加它的子节点-然后循环
			Node cur = queue.poll();
			//打印cur的值
			System.out.println(cur.value);
			//将cur的(有的话)子左、右节点添加到列表中
			
			if(cur.left != null) {
				queue.add(cur.left);
			}
			if(cur.right != null) {
				queue.add(cur.right);
			}
		}
	}
	
	                             //以层次方式遍历堆(Map表),输出堆和最大节点数的层次--- 根据定位在哪层,再求多少节点,再对比
	public static void maxWidthUseMap(Node head) {
		if(head ==null) return;
		
		//创建一个列表:并将头节点head元素放入列表中
		Queue<Node> queue = new LinkedList<>();
		queue.add(head);
		
		//创建一个HashMap表,用来存储 (堆位置,及对应层)- 以(key,value)键值对的方式
		HashMap<Node,Integer> levelMap = new HashMap<>();
		levelMap.put(head, 1);           //将(头节点head,对应层1)放进Map表---put:存储
		int curLevel = 1;                //正在进行的是第几层,开始是head的第1层
		int curLevelNodes = 0;           //这层有多少节点数 ,(每层开始时都为0 -每次遍历一个节点+1)
		int max = 0;
		
		while(!queue.isEmpty()) {
			Node cur = queue.poll();     //cur:当前堆位置---开始从最高头节点开始
			int curNodeLevel = levelMap.get(cur);    //get方法:Map表-输入key(cur-堆位置)-返回value(在哪层)
			
			if(cur.left != null) {
				//有子左节点,将 子左节点 和 对应层 存储在Map表中---知晓父节点层也就知晓子左、右节点层
				levelMap.put(cur.left, curNodeLevel+1);
				queue.add(cur.left);                        //添加子左节点在堆里
			}
			if(cur.right != null) {
				levelMap.put(cur.right, curNodeLevel+1);
				queue.add(cur.right);
			}
			
			//当 目前节点的层  =  正在进行的层  (即 目前节点 还在 当前进行的层 之中)
			if(curNodeLevel == curLevel) { 
				curLevelNodes++;                 //将 当前层节点数+1
			}else {                              //即 目前节点 到达 下一层 时
				max =Math.max(max, curLevelNodes);          //和 之前的层节点数最大值 做比较
				curLevel++;                                 //同时进行下一层 的循环语句执行
				curLevelNodes=1;                            //将节点数赋值为1(重新开始记录下一层)
			}
		}
		max=Math.max(max, curLevelNodes);                   //由于最后一次不会执行循环(没有比最后一层更低的了),所以在此 比较层节点数大小
		System.out.println(max);
	}
}




                           						//层次遍历堆-不用Map表、且输出最大节点数层
public static void pre2(Node head) {
	if(head!=null)return;
	
	//创建列表,并添加head
	Queue<Node> queue = new LinkedList<>();
	queue.add(head);
	
	Node curEnd = head;       //当前层的最后节点:
	Node nextEnd = null;      //下一层的最后节点:
	int max=0,curLevelNodes=0;       //当前层的节点数
	
	while(!queue.isEmpty()) {
		Node cur = queue.poll();           //当前的打印节点
		if(cur.left!=null) { 
			queue.add(cur.left);           //添加到列表
			nextEnd = cur.left;            //并让 下一层最后节点 指向 cur.left
		}
		if(cur.right!=null) {
			queue.add(cur.right);
			nextEnd = cur.right;
		}
		curLevelNodes++;                   //即当前层节点数++;
		
		//当前节点cur = curEnd当前层的最后节点
		if(cur == curEnd) {
			max=Math.max(max, curLevelNodes);       //与之前层节点数做比较
			curLevelNodes = 0;                      //同时准备记录下一层的节点数
			curEnd = nextEnd;                       //开始下一层语句执行
		}
	}
	System.out.println(max);
}





										//序列化堆---反序列堆(原理差不多-先判断是否为空、长度为0,再将String值转换Integer赋值给head,再递归)

public static Queue<String> preSerial(Node head) {         //queue 的类型:Queue<Node>
	Queue<String> queue = new LinkedList<>();
	pres(head,queue);              //调用pres方法---因为有创建序列队列,所以把递归方法放在外面
	return queue;
}
public static void pres(Node head,Queue<String> queue) {
	if(head == null) {       //序列化在最近的null上要添加
		queue.add(null);
	}else {
		queue.add(String.valueOf(head.value));     //将head.value的值 转换成 字符串 形式放在 序列化 里[1,null,31]这样
		pres(head.left,queue);          //递归---同样的把queue.add放中间就是中序
		pres(head.right,queue);
	}
}



                   					//按层次序列化堆---即有节点(+队列+序列,),无节点(+序列-不加队列)
public static Queue<String> levelSerial(Node head) {
	Queue<String> queue = new LinkedList<>();          //创建堆 序列化表 queue
	if(head == null ) {
		queue.add(null);                               //头节点为空-序列表输出null
	}else {
		queue.add(String.valueOf(head.value));         //否则序列表打印转换类型的头节点
		
		//创建 堆列表 queue1
		Queue<Node> queue1 = new LinkedList<>();       
		queue1.add(head);                              //同时在堆列表中也打印头节点
		
		//当堆列表不为空
		while(!queue1.isEmpty()) {
			head = queue1.poll();                      //创建要打印的头节点
			if(head.left!=null) {
				//输出不为空的head.left(二个列表都添加)
				queue.add(String.valueOf(head.left.value));
				queue1.add(head.left);
			}else {
				//head.left为空-输出 序列化表的null
				queue.add(null);
			}
			if(head.right!=null) {
				queue.add(String.valueOf(head.right.value));
				queue1.add(head.right);
			}else {
				queue.add(null);
			}
		}
	}
	return queue;
}}