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;
}}