二叉树
递归遍历二叉树
递归序:1,2,4,4,4,2,5,5,5,2,1,3,6,6,6,3,7,7,7,3,1
所谓递归序在二叉树中是指:遍历二叉树时经历的路径
如下代码,根据递归序,使用递归遍历二叉树
public class Traversal<T> {
private static class Node<E> {
public E value;
public Node left;
public Node right;
public Node(E value) {
this.value = value;
}
}
public void f(Node<T> head) {//演示递归序产生时间
if (head == null) {
return;
}
//递归序中的第一次到达
f(head.left);
//第二次到达
f(head.right);
//第三次到达
}
public void preOrder(Node<T> head) {//前序遍历
if (head == null) {
return;
}
System.out.println(head.value + " ");
preOrder(head.left);
preOrder(head.right);
}
public void inOrder(Node<T> head) {//中序遍历
if (head == null) {
return;
}
preOrder(head.left);
System.out.println(head.value + " ");
preOrder(head.right);
}
public void posOrder(Node<T> head) {//后序遍历
if (head == null) {
return;
}
preOrder(head.left);
preOrder(head.right);
System.out.println(head.value + " ");
}
}
非递归遍历二叉树
先序遍历:
1.每次从栈中弹出一个节点cur
2.处理(打印)cur
3.先右后左
4.重复执行
public void preOrderU(Node<T> head) {
if (head != null) {
Stack<Node> stack = new Stack<>();
stack.add(head);
while (!stack.isEmpty()) {
head = stack.pop();//先把头结点压入栈中
System.out.println(head.value + " ");//先输出头结点
if (head.right != null) {//先压右
stack.push(head.left);
}
if (head.left != null) {//后压左
stack.push(head.right);
}
}
}
}
中序遍历
1.先把左边界全进栈
2.依次弹出节点的过程中,打印,对弹出节点的右树循环往复
public void inOrderU(Node<T> head) {
if (head != null) {
Stack<Node> stack = new Stack<>();
stack.add(head);
while (!stack.isEmpty() || head != null) {
if (head != null) {//先把左边界全压入栈中
stack.push(head);
head = head.left;
} else {//依次弹出节点的过程中,打印,对弹出节点的右树循环往复
head = stack.pop();
System.out.println(head.value + " ");
head = head.right;
}
}
}
}
后续遍历
与前序遍历顺序相反
public void posOrderU(Node<T> head) {
if (head != null) {
Stack<Node> stack1 = new Stack<>();
Stack<Node> stack2 = new Stack<>();
stack1.add(head);
while (!stack1.isEmpty()) {
head = stack1.pop();//先把头结点压入栈中
stack2.push(head);//弹出顺序压入stack2
System.out.println(head.value + " ");//先输出头结点
if (head.right != null) {//先压右
stack1.push(head.left);
}
if (head.left != null) {//后压左
stack1.push(head.right);
}
}
while (!stack2.isEmpty()){//将Stack2中收集到的全输出
System.out.println(stack2.pop().value);
}
}
}
二叉树的宽度优先遍历
public void w(Node<T> head){
if (head == null){
return;
}
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);
}
}
}
得到二叉树的最大宽度
public int w1(Node<T> head) {
if (head == null) {
return 0;
}
Queue<Node> queue = new LinkedList<>();
HashMap<Node<T>, Integer> levelMap = new HashMap<>();//用来记录节点和节点所在层数
levelMap.put(head, 1);//先把头结点加入到map中
int cruLevel = 1;//层数
int cruLevelNodes = 0;//记录该层节点数目
int max = Integer.MIN_VALUE;//记录一层最多节点数
queue.add(head);
while (!queue.isEmpty()) {
Node cur = queue.poll();//父节点出队列
int cruNodeLevel = levelMap.get(cur);//得到节点层数
if (cruLevel == cruNodeLevel) {//如果层数与节点层数相同
cruLevelNodes++;//记录每层节点数自增
} else {//不相等时,说明cruNodeLevel来到下一层
max = Math.max(max, cruNodeLevel);//那么就可以更新max的值
cruLevel++;//层数自增
cruLevelNodes = 1;//因为此时已经是取出的下一层的第一个节点,所以直接记录为1
}
//遍历并入队节点的左右孩子
if (cur.left != null) {
queue.add(cur.left);
}
if (cur.right != null) {
queue.add(cur.right);
}
}
return max;
}
二叉树的递归套路
分析你需要从左右树拿到什么东西,取一个全集
判断一棵二叉树是否为搜索二叉树
搜索二叉树:左孩子都比该节点值小,右孩子都比该节点大
利用中序遍历,判断
public boolean isBST(Node head) {//判断是否为搜索二叉树
if (head == null) {
return true;
}
boolean temp = isBST(head.left);//检查左树是否为搜索二叉树
if (!temp) {//如果左边不是搜索二叉树,返回false
return false;
}
if (head.value <= preValue) {//比较节点的值是否大于前一次记录的值,如果小于,则返回false
return false;
} else {//否则记录最大值
preValue = head.value;
}
return isBST(head.right);//最后处理右树
}
根据递归套路的方法
需求:左右树是否为搜索二叉树,左边最大值小于该节点值,右边最小值大于该节点
public static class ReturnData {
boolean isBST;
int min;
int max;
public ReturnData(boolean isBST, int min, int max) {
this.isBST = isBST;
this.min = min;
this.max = max;
}
}
public ReturnData process1(Node x) {
if (x == null) {
return null;
}
ReturnData left = process1(x.left);//得到左边返回
ReturnData right = process1(x.right);//得到右边返回
int min = x.value;//记录最小值
int max = x.value;//记录最大值
if (left != null) {//当右边返回不是null,就更新min,max
min = Math.min(min, left.min);
max = Math.max(max, left.max);
}
if (right != null) {//当右边返回不是null,就更新min,max
min = Math.min(min, right.min);
max = Math.max(max, right.max);
}
boolean isBST = true;
//判断是否为搜索二叉树,左右不为空的情况下,左右不是搜索二叉树或者不满足搜索二叉树定义,isBST = false
if (left != null && (!left.isBST || left.max >= x.value)) {
isBST = false;
}
if (right != null && (!right.isBST || right.min < x.value)) {
isBST = false;
}
return new ReturnData(isBST, min, max);
}
判断一棵二叉树是否为完全二叉树
不是完全二叉树的条件:
1.如果有右孩子没有左孩子
2.不违反1的情况下,如果遇到了第一个左右孩子不双全的情况,那么接下来遇到的所有节点都必须是叶节点
public boolean is(Node head) {
if (head == null) {
return true;
}
Queue<Node> queue = new LinkedList<Node>();
queue.add(head);
boolean leaf = false;//用一个变量记录事件是否发生
Node l = null;//左节点
Node r = null;//右节点
while (!queue.isEmpty()) {
head = queue.poll();
l = head.left;
r = head.right;
if ((l == null && r != null) ||//条件1
(leaf && !(r == null && l == null))) {//条件2,事件发生后,该节点的只能是叶节点,所以无孩子节点
return false;
}
if (l != null) {
queue.add(l);
}
if (r != null) {
queue.add(r);
}
if (r == null || l == null) {
leaf = true;
}
}
//如果这棵树遍历结束,那么必须为完全二叉树
return true;
}
判断一棵二叉树是否为满二叉树
得到树的深度l,再得到这棵树的节点个数n,如果满足 n = 2^l - 1;即为满二叉树
使用递归套路:
判断需要向左右树获取节点所在高度,节点数量
public InFo proce(Node x) {
if (x == null) {
//当节点为null时,返回高度为0,节点数为0
return new InFo(0, 0);
}
InFo left = proce(x.left);
InFo right = proce(x.right);
//目前树的高度为左右两边最大值加一
int height = Math.max(left.height, right.height) + 1;
//目前节点个数为左右节点数相加后 + 1
int nodes = left.nodes + right.nodes + 1;
return new InFo(height, nodes);
}
public boolean isF(Node x) {
if (x == null) {
return true;
}
InFo inFo = proce(x);
//即判断 节点数是否为层数的平方 -1
//处理2的次方时,使用位运算符,1左移n位即为2的n次方
return inFo.nodes == (1 << inFo.height - 1);
}
判断一棵树是否为平衡二叉树
平衡二叉树:任意节点的左右两个子树高度差不超过一
public boolean isBalanced(Node head) {
return process(head).isBalanced;
}
//既然需要拿到高度,是否为平衡二叉树,那么就把这两个元素包装起来,用于返回
public static class ReturnType {
public int height;
public boolean isBalanced;
public ReturnType(int height, boolean isBalanced) {
this.height = height;
this.isBalanced = isBalanced;
}
}
public ReturnType process(Node x) {
if (x == null) {//递归结束条件
return new ReturnType(0, true);
}
ReturnType left = process(x.left);//拿到左树返回的高度,是否为平衡二叉树
ReturnType right = process(x.right);//拿到右树返回的高度,是否为平衡二叉树
int height = Math.max(left.height, right.height) + 1;//现在的树高度(左右树的最大值 + 1)
boolean isBalanced = left.isBalanced && right.isBalanced//左右树都是平衡二叉树
&& Math.abs(left.height - right.height) <= 1;//左右树高度差小于等于1
return new ReturnType(height, isBalanced);//返回每次判断结果
}