文章目录
二叉树模版
据不完全总结,二叉树的题大致可以分为两种,一种是通过前序、中序、后序、层序遍历来解决问题。
另一种为递归问题,需要从每个节点来获取信息,然后提取出题目中要求的信息
遍历模版
二叉树非递归遍历
中序
- 先找到最左节点,并逐步压栈
- 当最左为空时,弹出栈顶(此时为最左节点),并输出
- 找最左节点有没有右孩子,有则压栈(循环1,2),没有进行下一步
- 没有右孩子时,当前节点为null,弹出栈顶(此时栈顶为最左节点的父亲节点)
- 输出 最左节点的父亲节点 的值
- 找 5中的节点有没有右孩子 重复(1,2)
- 当栈为空 且 当前遍历的节点为null 时 ,遍历结束
public List<Integer> inorderTraversal(TreeNode root) {
if(root==null){
return new ArrayList<>();
}
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack=new Stack<>();
while(!stack.isEmpty() || root!=null){
if(root!=null){
stack.push(root);
root=root.left;
}else{
root=stack.pop();
list.add(root.val);
root=root.right;
}
}
return list;
}
前序
public List<Integer> preorderTraversal(TreeNode root) {
if(root==null){
return new ArrayList<>();
}
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack=new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
if(node.right!=null){
stack.push(node.right);
} list.add(node.val);
if(node.left!=null){
stack.push(node.left);
}
}
return list;
}
后序
public List<Integer> postorderTraversal(TreeNode root) {
if(root==null){
return new ArrayList<>();
}
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack=new Stack<>();
Stack<Integer> temp =new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
if(node.left!=null){
stack.push(node.left);
}
temp.push(node.val);
if(node.right!=null){
stack.push(node.right);
}
}
while(!temp.isEmpty()){
list.add(temp.pop());
}
return list;
}
层次遍历非递归
很多求层相关的问题都可以使用此模版解题
public List<List<Integer>> resList = new ArrayList<List<Integer>>();
public List<List<Integer>> levelOrder(TreeNode root) {
checkFun02(root);
return resList;
}
public void checkFun02(TreeNode node) {
if (node == null) return;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);
while(!que.isEmpty()){
List<Integer> itemList = new ArrayList<Integer>();
int len = que.size();
while (len > 0) {
TreeNode tmpNode = que.poll();
itemList.add(tmpNode.val);
if (tmpNode.left != null) que.offer(tmpNode.left);
if (tmpNode.right != null) que.offer(tmpNode.right);
len--;
}
resList.add(itemList);
}
}
注意事项
求二叉树对称的时候就是把两个树的节点同时加入队列进行比较
遇到在二叉搜索树上求什么最值,求差值之类的,都要思考一下二叉搜索树可是有序的,要利用好这一特点
迭代法中,一般一起操作两个树都是使用队列模拟类似层序遍历,同时处理两个树的节点,这种方式最好理解,如果用模拟递归的思路的话,要复杂一些。
大多数二叉搜索树的题目,其实都离不开中序遍历,因为这样就是有序的
递归模版
可以解决面试中绝大多数的二叉树问题尤其是树型dp问题
本质是利用递归遍历二叉树的便利性
递归套路步骤
- 假设以X节点为头,假设可以向X左树和X右树要任何信息
- 在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
- 列出所有可能性后,确定到底需要向左树和右树要什么样的信息
- 把左树信息和右树信息求全集,就是任何一 一棵子树都需要返回的信息S
- 递归函数都返回S,每一棵子树都这么要求
- 写代码,在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息
判断是否为平衡二叉树
给定一棵二叉树的头节点head,返回这颗二叉树是不是平衡二叉树
平衡二叉树(Balanced Binary Tree)又被称为AVL树:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
列可能性:
1.只有左、右子树都为平衡二叉树,且俩个子树高度相差为的绝对值小于1,这个树才是平衡二叉树
要信息:
1.左树,高度? 平衡否
2.右树,高度? 平衡否
public static boolean isBalanced2(Node head) {
return process2(head) .isBalaced;
}
//左、右要求一样,Info信息返回的结构体
public static class Info {
public boolean isBalaced;
public int height;
public Info(boolean b, int h) {
isBalaced = b;
height = h;
}
}
public static Info process2(Node X) {
if (X == nu1l) {
// 必须返回自己的信息
return new Info(true, 0);
}
Info leftInfo = process2(X.1eft);
Info rightInfo = process2(X. right);
int height = Math. max(1eftInfo . height, rightInfo.height) + 1;
boolean isBalanced = true;
if (!leftInfo.isBalaced||
! rightInfo. isBalaced||
Math. abs(leftInfo. height - rightInfo.height)>1
){
isBalanced = false;
}
return new Info(isBalanced, height);
}
返回整棵二叉树的最大距离
给定一棵二叉树的头节点head,任何两个节点之间都存在距离,
返回整棵二叉树的最大距离
/pic:mw://088ba329253e8a81d284465b9d7d838b
1.情况分类
如果最大距离与当前节点无关,
最大距离就是,要么是左树最大距离,要么右树最大距离
如果最大距离与当前节点有关
左树最远节点经过当前节点,到达最右节点,即为最大距离
2.返回信息
左树,返回最大距离
右树,返回最大距离
当前节点,返回 左树、右树、左树加根节点加右树,三者最大的
public static int maxDistance2(Node head) {
return process(head) . maxDistance;
}
public static class Info {
public int maxDistance;
public int height;
public Info(int dis, int h) {
maxDistance = dis;
height = h;
}
public static Info process(Node head) {
if (head == nu1l) {
return new Info(0, 0);
Info leftInfo = process(head.left);
Info rightInfo = process(head. right);
int height = Math .max(leftInfo. height, rightInfo. height) + 1;
int maxDistance = Math . max(
Math . max(leftInfo . maxDistance, rightInfo. maxDistance),
leftInfo. height + rightInfo.height + 1);
return new Info( maxDistance, height);
}
返回这颗二叉树中最大的二叉搜索子树的节点数
给定一棵二叉树的头节点head,
返回这颗二叉树中最大的二叉搜索子树的节点数
搜索二叉树:整个树上没有重复值,左树的值比根小,右树比根大,每颗子树都如此
1.可能性
与当前节点有关, 子树最大二叉搜索子树的节点+根节点
要求左搜存在,右搜存在,且左最大<根,根<右最小
与当前节点无关,左子树的最大二叉搜索子树,右子树的最大二叉搜索子树,取最大
2.信息
左树,最大子搜的size,是否平衡,左树的最大节点值
右树,最大子搜的size,是否平衡,右树的最小节点值
3.求全集
info 信息类定义为:最大子搜的size,是否平衡,子树最大节点值,子树最小节点值
pub1ic static int maxSubBSTSize2(Node head) {
if (head == nu1l) {
return 0;
return process( head) . maxSubBSTSize;
}
public static class Info {
public boolean isBST;
public int maxSubBSTSize;
public int min;
public int max;
public Info(boolean is, int size, int mi, int ma) {
isBST = is;
maxSubBSTSize = size;
min = mi;
max = ma;
}
public static Info process(Node X){
//不好设置初始信息,所以认为判空
if(X==null){
return null;
}
Info leftInfo = process(X.left);
Info rightInfo = process(X.right);
int min=X.value;
int max=X.value;
if(leftInfo!=null){
min=Math.min(min,leftInfo.min);
max=Math.max(max,leftInfo.max);
}
if(rightInfo!=null){
min=Math.min(min,rightInfo.min);
max=Math.max(max,rightInfo.max);
}
int maxSubBSTSize=0;
if(leftInfo!=null){
maxSubBSTSize=leftInfo.maxSubBSTSize;
}
if(rightInfo!=null){
maxSubBSTSize=Math.max(maxSubBSTSize,rightInfo.maxSubBSTSize);
}
boolean isBST=false;
//如果可能性二成立,需要更新maxSubBSTSize,且设置isBST
if(
// 左树整体是搜索二叉树
(leftInfo==null?true :leftInfo.isBST)
&&
// 右树整体是搜索二叉树
(rightInfo==null?true :rightInfo.isBST)
&&
// 左树最大值 《 x
(leftInfo==null?true : leftInfo.max <X.value)
&&
// 右树最小值 》x
(rightInfo==null?true : rightInfo.min >X.value)
){
// 以x为头的所有节点,只要用了信息就要判空
maxSubBSTSize=
(leftInfo == null ? 0 : leftInfo . maxSubBSTSize)
+
(rightInfo == nu1l ? 0 : rightInfo. maxSubBSTSize )
+
1;
isBST=true;
}
return new Info(isBST,maxSubBSTSize,min,max);
}
若求,最大二叉搜索树的头节点?
派对的最大快乐值
员工信息的定义如下:
class Employee {
public int happy;//这名员工可以带来的快乐值
List< Employee> subordinates; //这名员工有哪些直接下级
}
公司的每个员工都符合Employee类的描述。整个公司的人员结构可以看作
是一棵标准的、没有环的多叉树。树的头节点是公司唯一的老板。除老板之
外的每个员工都有唯一的直接上级。叶节点是没有任何下属的基层员工
(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。
这个公司现在要办party,你可以决定哪些员工来,哪些员工不来,规则:
1.如果某个员工来了,那么这个员工的所有直接下级都不能来
2.派对的整体快乐值是所有到场员工快乐值的累加
3.你的目标是让派对的整体快乐值尽量大
给定一棵多叉树的头节点boss,请返回派对的最大快乐值。
可能性
1.给x 发了邀请,即 所有直接子树不来的最大快乐值
2.没有给x 发邀请,所有子树的最大快乐值
需要的信息
该节点不参加的最大快乐值
该节点参加的最大快乐值
public static class Employee {
public int happy;
public List< Employee> nexts;
public Employee(int h) {
happy = h;
nexts = new
ArrayList<>();
}
public static class Info {
pub1ic int yes;
public int no;
public Info(int y, int n) {
yes = y;
no=n;
}
}
public static Info process2(Employee x) {
if (x.nexts. isEmpty() {
return new Info(x.happy, 0);
}
int yes = x.happy;
intno=0;
for (Employee next : x.nexts) {
Info nextInfo = process2(next);
yes += nextInfo. no;
no += Math .max(nextInfo.yes, nextInfo.no);
}
return new Info(yes, no);
}
给定一棵二叉树的头节点head,返回这颗二叉树是不是满二叉树
满二叉树
高度是L,节点树是n
满足 (2^L ) -1 = n
public static boolean isFul12(Node head) {
if (head == nu1l) {
return true ;
}
Info all = process(head);
return (1 << all.height) - 1 == all.nodes;
}
public static class Info {
public int height;
public int nodes;
public Info(int h, int n) {
height = h;
nodes = n;
}
}
public static Info process(Node head) {
if (head == nu1l) {
return new Info(0, 0);
Info leftInfo = process (head.1eft);
Info rightInfo = process(head. right);
int height = Math .max(leftInfo. height, rightInfo.height) + 1;
int nodes = leftInfo.nodes + rightInfo.nodes + 1;
return new Info(height, nodes);
}
返回这颗二叉树中最大的二叉搜索子树的头节点
pub1ic static Node maxSubBSTHead2(Node head) {
if (head == nu1l) {
return nu11 ;
}
return process(head) . maxSubBSTHead;
}
pub1ic static class Info {
public Node maxSubBSTHead;
public int maxSubBSTSize;
public int min;
public int max;
public Info(Node h, int size, int mi, int ma) {
maxSubBSTHead = h ;
maxSubBSTSize = size;
min = mi;
max = ma;
}
}
public static Info process(Node X){
if (X == null) {
return null ;
}
Info leftInfo = process(X.left);
Info rightInfo= process(X.right);
int min = X. value;
int max = X. value ;
Node maxSubBSTHead = null;
int maxSubBSTSize = 0;
if (leftInfo != nu1l) {
min = Math . min(min, leftInfo. min);
max = Math . max(max, leftInfo. max);
maxSubBSTHead = leftInfo . maxSubBSTHead;
maxSubBSTSize = leftInfo. maxSubBSTSize ;
}
if (rightInfo != null) [
min = Math. min(min, rightInfo .min);
max = Math . max(max, rightInfo . max);
if (rightInfo. maxSubBSTSize > maxSubBSTSize) {
maxSubBSTHead = rightInfo. maxSubBSTHead;
maxSubBSTSize = rightInfo . maxSubBSTSize;
}
}
if ((leftInfo == null ? true : (leftInfo.maxSubBSTHead == X.left && leftInfo.max < X.value))
&& (rightInfo == null ? true : (rightInfo.maxSubBSTHead == X.right && rightInfo.min > X.value))) {
maxSubBSTHead = X;
maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize)
+ (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
}
return new Info(maxSubBSTHead, maxSubBSTSize, min, max);
}
给定一棵二叉树的头节点head,返回这颗二叉树中是不是完全二叉树
完全二叉树:一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
层次遍历
1.任意节点,有右无左 false,否则继续
2.一旦遇到左右孩子不双全,接下来的所有节点都必须是叶节点
public static boolean isCBT1(Node head) [
if (head == nu1l) (
return true ;
]
LinkedList<Node> queue = new LinkedList<>();
boolean leaf = false;
Node l = null;
Node r = null;
queue . add(head);
while (!queue . isEmpty()) {
head = queue. pol1();
1 = head.left;
r = head.right;
if (
//如果遇到了不双全的节点之后,又发现当前节点不是叶节点
(leaf && !(l == null && r == null)) || (l == null && r != null)) (
return false;
]
if (l != null) (
queue .add(l);
]
if (r != null) {
queue.add(r);
]
if (l == null r == nu1l) {
leaf = true ;
}
}
return true
}
递归
根据叶节点到达的位置
1.满二叉树=完全二叉树,无缺口
2.缺口停在左树,有缺口
3.缺口在右树,但没接触到左树的最右子树
4.缺口在右树,且接触到了左树的最右子树
需求信息:
左树,是否满二叉树,及高度
左树,是否满二叉树,及高度
1俩树都满
2 左树完全二叉树,右树满二叉树,且左树高度-1等于右树高度
3左树满二叉树,右树满二叉树,且左树高度-1等于右树高度
4左树满的,右树完全,高度相等
四种情况属于一种即是完全二叉树
public static boolean isCBT2(Node head) (
if (head == null) [
return true ;
]
return process (head) .isCBT;
]
//对每一棵子树,1是否是满二叉树、是否是完全二叉树、高度|
public static class Info {
public boolean isFull;
public boolean isCBT ;
public int height;
public Info(boolean full, boolean cbt,
int h) {
isFull = full;
isCBT = cbt;
height = h;
]
]
public static Info process(Node X) [
if (X == null) 1
return new Info(true, true, 0);
}
Info leftInfo = process(X.left);
Info rightInfo = process(X.right);
int height = Math . max(leftInfo . height, rightInfo.height) + 1;
boolean isFull = leftInfo. isFull && rightInfo. isFull && leftInfo.height == rightInfo.height;
boolean isCBT = false;
if (isFull) f
isCBT = true;
] Ielse (
if (leftInfo.isCBT && rightInfo.isCBT) {
if (leftInfo.isCBT && rightInfo. isFull && leftInfo.height == rightInfo.height+1
isCBT = true;
]
if (leftInfo. isFull && rightInfo. isFull && leftInfo. height == rightInfo.height+1
isCBT = true;
]
if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height
isCBT = true;
}
}
}
return new Info(isFull, isCBT, height);
}
返回a和b的最低公共祖先
给定一-棵二叉树的头节点head,
和另外两个节点a和b。
返回a和b的最低公共祖先
map+set
建立 key 是当前节点, value 是父节点的map
然后将当前节点加入set,然后不断找父节点,直到根节点
将b节点的父节点加入set,每次判断set大小,第一次遇到重复的就是最近 公共祖先
public static Node lowestAncestor1(Node head, Node o1, Node o2) [
if (head== null) (
return null;
}
HashMap<Node, Node> parentMap = new HashMap<>();
parentMap. put(head, null);
filLParentMap(head, parentMap);
HashSet<Node> o1Set = new HashSet<>( );
Node cur = o1;
o1Set.add(cur);
While (parentMap.get(cur) != null) [
cur = parentMap. get(cur);
o1Set. add(cur);
]
cur = o2;
while (!o1Set. contains(cur)) f
cur = parentMap. get(cur);
return cur ;
}
public static void fillParentMap(Node helad, HashMap<Node, Node> parentMap) {
if (head.left != null) [
parentMap . put(head.left, head);
fillParentMap(head .left, parentMap);
}
if (head. right!= null){
parentMap. put (head.right, head);
fillParentMap(head . right, parentMap);
}
}
递归
列可能
1.o1 和 o2 都不在当前节点上
2.o1 或 o2 只有一个在x 上
3 o1 和 o2 都在x上
1》左右各一个
2》都在左树
3 都在右树
4》x 是o1 ,左树找o2
x是o2 右树找o1
public static Node lowestAncestor2(Node head, Node a, Node b) {
return process(head, a, b).ans;
}
public static class Info {
public boolean findA;
public boolean findB;
// 子树最初交汇点
public Node ans;
public Info(boolean fA, boolean fB, Node an) {
findA = fA;
findB = fB;
ans = an;
}
}
public static Info process(Node x, Node a, Node b) {
if (x == null) {
return new Info(false, false, null);
}
Info leftInfo = process(x.left, a, b);
Info rightInfo = process(x.right, a, b);
boolean findA = (x == a) || leftInfo.findA || rightInfo.findA;
boolean findB = (x == b) || leftInfo.findB || rightInfo.findB;
Node ans = null;
if (leftInfo.ans != null) {
ans = leftInfo.ans;
} else if (rightInfo.ans != null) {
ans = rightInfo.ans;
} else {
if (findA && findB) {
ans = x;
}
}
return new Info(findA, findB, ans);
}