目录
[一、94. 二叉树的中序遍历](#一、94. 二叉树的中序遍历)
[二、102. 二叉树的层序遍历](#二、102. 二叉树的层序遍历)
[三、104. 二叉树的最大深度](#三、104. 二叉树的最大深度)
[四、105. 从前序与中序遍历序列构造二叉树](#四、105. 从前序与中序遍历序列构造二叉树)
[五、543. 二叉树的直径](#五、543. 二叉树的直径)
[六、114. 二叉树展开为链表](#六、114. 二叉树展开为链表)
[七、226. 翻转二叉树](#七、226. 翻转二叉树)
[八、101. 对称二叉树](#八、101. 对称二叉树)
[九、617. 合并二叉树](#九、617. 合并二叉树)
要求:一些基本的数据结构的定义要能够手写代码出来
学习思路:
- 先去B站上看一些介绍二叉树基础知识的视频,了解二叉树大概涉及哪些内容,重难点又是什么
- 然后去力扣上搜二叉树相关的热门题单,从基础部分开始刷,从代码的角度了解二叉树的特性(刚开始不会就看答案,刷多了就熟了)
- 题目做出来后,再想办法优化自己的代码
- 代码看不懂,就去B站搜视频看别人讲解
常用二叉树:(掌握这四种,平常做题就够了)满二叉树
完全二叉树
二叉搜索树:对于二叉搜索树中的每个结点,其左子结点的值小于该结点的值,而右子结点的值大于该结点的值
平衡二叉搜索树:每个结点的子树
的高度差距为0或1
二叉树的三种遍历方式:(面试考点)
两大遍历方式:
- 深度优先搜索
- 广度优先搜索
两种遍历方式
- 递归法
- 迭代法
二叉树的三种遍历方式:(递归法)
java
//定义结点类
public class Node{
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value=data;
}
}
//前序遍历
public void preOrderRecur(Node head) {
if(head==null) {
return;
}
System.out.print(head.value+" ");
preOrderRecur(head.left);
preOrderRecur(head.right);
}
//中序遍历
public void inOrderRecur(Node head) {
if(head==null) {
return;
}
inOrderRecur(head.left);
System.out.print(head.value+" ");
inOrderRecur(head.right);
}
//后序遍历
public void posOrderRecur(Node head) {
if(head==null) {
return;
}
posOrderRecur(head.left);
posOrderRecur(head.right);
System.out.print(head.value+" ");
}
二叉树的三种遍历方式:(非递归法)
1、前序遍历
java
public void preOrderUnRecur(Node head) {
System.out.print("pre-order:");
if(head!=null) {
Stack<Node> stack=new Stack<Node>();
stack.add(head);//将头节点压入栈中
while(!stack.isEmpty()) {
head=stack.pop();//弹出栈顶节点
System.out.print(head.value+" ");
if(head.right!=null) {
stack.push(head.right);//压入当前节点的右孩子节点
}
if(head.left!=null) {
stack.push(head.left);//压入当前节点的左孩子节点
}
}
}
System.out.println();
}
2、中序遍历
java
public void inOrderUnRecur(Node head) {
System.out.print("in-order:");
if(head!=null) {
Stack<Node> stack=new Stack<Node>();
while(!stack.isEmpty() || head!=null) {
if(head!=null) {
stack.add(head);
head=head.left;
}else {
head=stack.pop();
System.out.print(head.value+" ");
head=head.right;
}
}
}
System.out.println();
}
3、后序遍历
java
public void posOrderUnRecur(Node head) {
System.out.print("pos-order:");
if(head!=null) {
Stack<Node> s1=new Stack<Node>();
Stack<Node> s2=new Stack<Node>();
s1.push(head);
while(!s1.isEmpty()) {
head=s1.pop();
s2.push(head);
if(head.left!=null) {
s1.push(head.left);
}
if(head.right!=null) {
s1.push(head.right);
}
}
while(!s2.isEmpty()) {
System.out.print(s2.pop().value+" ");
}
}
System.out.println();
}
二叉树的广度优先搜索:
迭代法:
java
public void levelOrder(Node head) {
System.out.print("level-order: ");
if (head != null) {
Queue<Node> queue = new LinkedList<>(); // 使用队列保存节点,先进先出
queue.offer(head); // 将头节点加入队列
while (!queue.isEmpty()) {
head = queue.poll(); // 弹出队列头部节点
System.out.print(head.value + " ");
if (head.left != null) {
queue.offer(head.left); // 将左子节点加入队列
}
if (head.right != null) {
queue.offer(head.right); // 将右子节点加入队列
}
}
}
System.out.println();
}
二叉树的深度优先搜索:
一、94. 二叉树的中序遍历
法一:递归法(套用上面的模板)
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
List<Integer> list=new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root==null){
return list;
}
inorderTraversal(root.left);
list.add(root.val);
inorderTraversal(root.right);
return list;
}
}
法二:迭代法
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<>();
if(root!=null){
Stack<TreeNode> stack=new Stack<TreeNode>();
while(!stack.isEmpty() || root!=null){
if(root!=null){
stack.add(root);
root=root.left;
}else{
root=stack.pop();
list.add(root.val);
root=root.right;
}
}
}
return list;
}
}
二、102. 二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
示例 2:
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
提示:
树中节点数目在范围 [0, 2000] 内
-1000 <= Node.val <= 1000
难点在于,如何将同一层节点的值加入到同一个子链表
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>(); // 用于存放最终的层序遍历结果
if (root == null) {
return result;
}
Queue<TreeNode> queue = new LinkedList<>(); // 使用队列辅助进行层序遍历
queue.offer(root); // 将根节点加入队列
while (!queue.isEmpty()) {
int levelSize = queue.size(); // 当前层的节点数
List<Integer> currentLevel = new ArrayList<>(); // 存放当前层的节点值
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll(); // 出队一个节点
currentLevel.add(node.val); // 将节点值加入当前层列表
if (node.left != null) {
queue.offer(node.left); // 左子节点入队
}
if (node.right != null) {
queue.offer(node.right); // 右子节点入队
}
}
result.add(currentLevel); // 当前层遍历结束,将当前层节点值列表加入最终结果
}
return result;
}
}
三、104. 二叉树的最大深度
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:3
示例 2:
输入:root = [1,null,2]
输出:2
提示:
树中节点的数量在 [0, 104] 区间内。
-100 <= Node.val <= 100
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {//使用深度优先搜索(DFS)/递归
if (root == null) {
return 0;
} else {
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
}
}
四、105. 从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1]
输出: [-1]
提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder 和 inorder 均 无重复 元素
inorder 均出现在 preorder
preorder 保证 为二叉树的前序遍历序列
inorder 保证 为二叉树的中序遍历序列
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
// 使用HashMap存储中序遍历结果的值和对应的索引,以便快速查找节点在中序遍历中的位置
HashMap<Integer, Integer> indexMap = new HashMap<>();
for (int i = 0; i < inorder.length; i++) {
indexMap.put(inorder[i], i);
}
return buildTree(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1, indexMap);
}
private TreeNode buildTree(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd, HashMap<Integer, Integer> indexMap) {
// 判断边界情况,如果起始位置大于结束位置,返回null
if (preStart > preEnd || inStart > inEnd) {
return null;
}
// 根据先序遍历结果确定根节点的值
int rootVal = preorder[preStart];
TreeNode root = new TreeNode(rootVal);
// 在中序遍历中找到根节点的位置
int rootIndex = indexMap.get(rootVal);
// 计算左子树的大小
int leftSubtreeSize = rootIndex - inStart;
// 递归构建左子树和右子树
root.left = buildTree(preorder, preStart + 1, preStart + leftSubtreeSize, inorder, inStart, rootIndex - 1, indexMap);
root.right = buildTree(preorder, preStart + leftSubtreeSize + 1, preEnd, inorder, rootIndex + 1, inEnd, indexMap);
return root;
}
}
五、543. 二叉树的直径
给你一棵二叉树的根节点,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。
两节点之间路径的 长度 由它们之间边数表示。
示例 1:
输入:root = [1,2,3,4,5]
输出:3
解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。
示例 2:
输入:root = [1,2]
输出:1
提示:
树中节点数目在范围 [1, 104] 内
-100 <= Node.val <= 100
分析:
在计算二叉树的直径时,需要考虑的情况是直径路径可能经过根节点,也可能不经过根节点。因此,我们在计算直径时需要分别考虑这两种情况:
- 如果直径路径经过根节点:直径长度为左子树的最大深度加上右子树的最大深度。
- 如果直径路径不经过根节点:直径在左子树或右子树中,而不经过根节点。
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int diameterOfBinaryTree(TreeNode root) {
if (root == null) {
return 0;
}
//我们需要调用 maxDepth 方法来计算左右子树的深度,同时还需要递归地计算左右子树的直径
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
int leftDiameter = diameterOfBinaryTree(root.left);
int rightDiameter = diameterOfBinaryTree(root.right);
return Math.max(leftDepth + rightDepth, Math.max(leftDiameter, rightDiameter));
}
public int maxDepth(TreeNode root){
if(root==null){
return 0;
}else{
int leftDepth=maxDepth(root.left);
int rightDepth=maxDepth(root.right);
return Math.max(leftDepth,rightDepth)+1;
}
}
}
六、114. 二叉树展开为链表
给你二叉树的根结点
root
,请你将它展开为一个单链表:
- 展开后的单链表应该同样使用
TreeNode
,其中right
子指针指向链表中下一个结点,而左子指针始终为null
。- 展开后的单链表应该与二叉树 先序遍历 顺序相同。
示例 1:
输入:root = [1,2,5,3,4,null,6] 输出:[1,null,2,null,3,null,4,null,5,null,6]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [0] 输出:[0]
提示:
树中结点数在范围
[0, 2000]
内
-100 <= Node.val <= 100
分析:把根部和右侧断开,把左侧塞进去后,再接好,再断,再接......
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public void flatten(TreeNode root) {
// 递归结束条件
if (root == null) {
return;
}
// 展开左子树
flatten(root.left);
// 展开右子树
flatten(root.right);
// 保存右子树(相当于根部和右侧断开,把左侧塞进去后,再接好)
TreeNode temp = root.right;
// 将左子树接到根节点的右侧
root.right = root.left;
root.left = null;
// 找到新右子树现在的最右节点
while (root.right != null) {
root = root.right;
}
// 将之前保存的右子树接到现在的最右节点上
root.right = temp;
}
}
七、226. 翻转二叉树
给你一棵二叉树的根节点
root
,翻转这棵二叉树,并返回其根节点。示例 1:
输入:root = [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]
示例 2:
输入:root = [2,1,3] 输出:[2,3,1]
示例 3:
输入:root = [] 输出:[]
提示:
树中节点数目范围在
[0, 100]
内
-100 <= Node.val <= 100
分析:与上面第六题类似
从最上面的根节点开始,往下,交换左右子树
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null){
return root;
}
invertTree(root.left);
invertTree(root.right);
//交换左右子树
TreeNode temp=root.right;
root.right=root.left;
root.left=temp;
return root;
}
}
八、101. 对称二叉树
给你一个二叉树的根节点
root
, 检查它是否轴对称。示例 1:
输入:root = [1,2,2,3,4,4,3] 输出:true
示例 2:
输入:root = [1,2,2,null,3,null,3] 输出:false
提示:
- 树中节点数目在范围
[1, 1000]
内-100 <= Node.val <= 100
递归法:
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
return isMirror(root, root);
}
public boolean isMirror(TreeNode t1, TreeNode t2) {
//两个节点同时为空,停止递归
if (t1 == null && t2 == null) {
return true;
}
//两个节点不同时为空,一定不对称
if (t1 == null || t2 == null) {
return false;
}
// 判断当前节点的值是否相等,并且左子树的左子树与右子树的右子树、左子树的右子树与右子树的左子树是否镜像对称
return (t1.val == t2.val) && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left);
}
}
迭代法:
java
class Solution {
public boolean isSymmetric(TreeNode root) {
// 创建一个队列,用于迭代处理节点
Queue<TreeNode> queue = new LinkedList<>();
// 初始时将根节点入队两次,分别走左边、右边
queue.add(root);
queue.add(root);
// 迭代处理队列中的节点
while (!queue.isEmpty()) {
// 每次从队列中取出两个节点进行比较
TreeNode t1 = queue.poll();
TreeNode t2 = queue.poll();
// 如果两个节点同时为null,则继续下一轮迭代
if (t1 == null && t2 == null) continue;
// 如果两个节点中有一个为null,或者节点值不相等,则返回false
if (t1 == null || t2 == null || t1.val != t2.val) return false;
// 队列是先进先出的,所以一次取两个,比较的是t1.left和t2.right
queue.add(t1.left);
queue.add(t2.right);
queue.add(t1.right);
queue.add(t2.left);
}
// 如果迭代完成后没有返回false,则说明树是对称的
return true;
}
}
九、617. 合并二叉树
给你两棵二叉树:
root1
和root2
。想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
示例 1:
输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7] 输出:[3,4,5,5,4,null,7]
示例 2:
输入:root1 = [1], root2 = [1,2] 输出:[2,2]
提示:
两棵树中的节点数目在范围
[0, 2000]
内
-104 <= Node.val <= 104
分析:把root1中多于root2的部分嫁接到root2中
最终把root2当结果返回
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
}
if (root2 == null) {//返回root1中不为空的部分,嫁接到root2中
return root1;
}
root2.val += root1.val;
root2.left = mergeTrees(root1.left, root2.left);//调用mergeTrees()方法返回的是子树
root2.right = mergeTrees(root1.right, root2.right);
return root2;
}
}
十、236. 二叉树的最近公共祖先
分析:
java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null || root==p || root==q){
return root;
}
TreeNode left=lowestCommonAncestor(root.left,p,q);
TreeNode right=lowestCommonAncestor(root.right,p,q);
if(left!=null && right!=null){
return root;
}
return left!=null?left:right;
}
}