二叉树
递归和非递归三种方式(PreOrder, InOrder, PostOrder)
递归
public static void f(Node head)
{
if (head == null)
{
return;
}
//先序遍历
System.out.print(head.value + " ");
f(head.left);
//中序遍历
System.out.print(head.value + " ");
f(head.right);
//后序遍历
System.out.print(head.value + " ");
}
非递归
先序遍历
1、从栈中弹出curr, 打印cur
2、先右孩子,再左孩子入栈,如果有的话
3、栈为空或者栈没有东西弹了,停止
public static void preOrderUnRecur(Node head)
{
System.out.println("preOrder:");
if (head != null)
//入栈条件是节点非空
{
Stack<Node> stack = new Stack<Node>();
stack.add(head);
while (!stack.empty())
{
head = stack.pop();
System.out.println(head.val + " ");
if (head.left != null)
{
stack.push(head.left);
}
if(head.right != null)
{
stack.push(head.right);
}
}
}
System.out.println();
}
后序遍历
先序遍历入栈顺序是中左右,中放到收集栈,左右入栈然后再出栈到收集栈
收集栈变成中右左再出栈就变成了左右中
public static void posOrderUnRecur(Node head)
{
System.out.println("PosOrder:");
if (head != null)
{
Stack<Node> stack1 = new Stack<Node>();
Stack<Node> stack2 = new Stack<Node>();
stack1.push(head);
while (!stack1.isEmpty())
{
head = stack1.pop();
stack2.push(head); //先序的打印变成压栈到s2
if (head.left != null)
{
stack1.push(head.left);
}
if (head.right != null)
{
stack1.push(head.right);
}
while(!stack2.isEmpty())
{
System.out.println(stack2.pop().value + " ");
}
}
}
System.out.println();
}
中序遍历
1、子树整条左边界依次进栈,如果没有进入阶段二
2、栈中弹出cur并打印,cur的右子树重复阶段一
public static void InOrderUnRecur(Node head)
{
System.out.println("InOrder:");
if (head != null)
{
Stack<Node> stack = new Stack<Node>();
Node cur = head;
while (!stack.isEmpty() || cur != null)
{
//当栈为空或者节点为空,跳出循环
if (cur != null)
{
stack.push(cur);
cur = cur.left;
}
else
{
cur = stack.pop();
System.out.println(cur.value + " ");
cur = cur.right;
}
}
}
System.out.println();
}
宽度遍历
队列顺序遍历
public static void process(Node head)
{
while (head != null)
{
Queue<Node> queue = new LinkedList<>();
queue.add(head);
while(!queue.isEmpty())
{
Node cur = queue.poll();
System.out.println(cur.value + " ");
if (cur.left != null)
{
queue.add(cur.left);
}
if (cur.right != null)
{
queue.add(cur.right);
}
}
}
System.out.println();
}
二叉树最大层数
在宽度遍历的基础上,用Map来存储对应节点的层数
用cLevel, cNodes, max – 当前层数,当前层节点数,最大节点数
public static void process1(Node head)
{
while (head != null)
{
HashMap<Node, Integer> levelMap = new HashMap<>();
Queue<Node> queue = new LinkedList<>();
queue.add(head);
levelMap.put(head, 1);
int cLevel = 1; // 当前层数
int cNodes = 0; // 当前层数节点数
int max = 0; //一层最大节点数
while(!queue.isEmpty() || head != null)
{
Node cur = queue.poll();
int level = levelMap.get(cur);
System.out.println("Node:" +cur.value + " Level: " + level);
if (level == cLevel)
{
cNodes ++;
}
else
{
//level != cLevel
max = Math.max(max, cNodes);
cLevel ++;
cNodes = 1;
}
if (cur.left != null)
{
queue.add(head.left);
levelMap.put(head.left, level + 1);
}
if (cur.right != null)
{
queue.add(head.right);
levelMap.put(head.left, level + 1);
}
}
// 最后一层
System.out.println("Node:" +cur.value + " Level: " + level);
max = Math.max(max, cNodes);
System.out.println(max);
}
}
二叉树的递归套路
搜索二叉树
搜索二叉树定义:左子树的值均小于右子树的值
中序遍历二叉树
在打印的时刻变成比较
public static int preValue = Integer.
完全二叉树
宽度遍历
①有右孩子没有左孩子返回false
②在满足一的条件下,遇到第一个左右孩子不双全的节点,后续都要是叶子节点才符合
满二叉树
节点数 N 最大深度为l
N = 2^l - 1
平衡二叉树
左树的高度和右树的高度差不超过1
一、左树平衡
二、右树平衡
三、|左树和右树的高度差|<=1
二叉树的前驱和后继节点
在二叉树中找到一个节点的后继节点
【题目】现在有一种新的二叉树节点类型如下:
public class Node {
public int value ;
public Node left;
public Node right;
public Node parent;
public Node(int val)
{
value = val ;
}
该结构比普通二叉树节点结构多了一个指向父节点的parent指针。
假设有一棵Node类型的节点组成的二叉树,树中每个节点的parent指针都正确地指向自己的父节点,头节点的parent指向null。
只给一个在二叉树中的某个节点node,请实现返回node的后继节点的函数。在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点。
public static Node getSuccessorNode(Node node)
{
//返回后继节点
if (node == null)
{
return null;
}
if (node.right != null)
{
// 有右孩子情况
return getLeftMost(node.right);
}
else
{
// 没有右子树情况
Node parent = node.parent;
while (parent != null && parent.left != node) {
// 当前节点是父母节点的右孩子
node = parent;
parent = node.parent;
}
return parent;
}
}
二叉树的序列化和反序列化
就是内存里的一棵树如何变成字符串形式,又如何从字符串形式变成内存里的树
作用:判断一颗二叉树是不是另一棵二叉树的子树?
public static String serialByPre(Node head)
{
//序列化
if (head == null)
{
return "#-";
}
String res = head.getValue() + "-";
res += serialByPre(head.left);
res += serialByPre(head.right);
return res;
}
public static Node reconByPreString(String preStr)
{
// 反序列化
String[] values = preStr.split("-");
Queue<String> queue = new LinkedList<String>();
for (int i = 0; i < values.length; i++)
{
queue.add(values[i]);
}
return reconByPreOrder(queue);
}
public static Node reconPreOrder(Queue<String> queue)
{
// 根据队列建出一棵树
String value = queue.poll();
if (value == "#")
{
return null;
}
Node node = new Node(Integer.valueOf(value));
head.left = reconPreOrder(queue);
head.right = reconPreOrder(queue);
return head;
}
折纸问题
分析我们可以得知
①第一个折痕是凹的
②一个折痕再对折一次上面的折痕是凹的,下面的折痕是凸的(节点的左孩子是凹的,右孩子是凸的)
public class 二叉树PaperFolding
{
public static void printAllFolds(int N)
{
printProcess(1, N, true);
}
private static void printProcess(int i, int N, boolean down)
{
// i层开始, N层结束, down为True表示凹
if (i > N)
{
return ;
}
printProcess(i + 1, N, true);
System.out.println(down ? "凹" : "凸");
printProcess(i + 1, N, false);
}
public static void main(String[] args)
{
int N = 3;
printAllFolds(N);
; }
}