
一.二叉树
1.相关概念



两种特殊的二叉树:

二叉树的性质:

那么已知孩子 父亲:(i-1)/2.
2.二叉树的存储

(1)链式存储


3.二叉树的基本操作
(1)二叉树的遍历=>递归+非递归


前序遍历:

java
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<>();
if(root==null){
return list;
}
list.add(root.val);
List<Integer> leftList=preorderTraversal(root.left);
list.addAll(leftList);
List<Integer> rightList=preorderTraversal(root.right);
list.addAll(rightList);
return list;
}
}
使用栈:
java
栈顶弹出即访问,先压右再压左
public void preOrderTraversal(TreeNode root) {
if (root == null) return;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
System.out.print(node.val + " ");
// 注意:栈是后进先出,所以先压右孩子,再压左孩子
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
}
中序遍历:

java
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<>();
if(root==null){
return list;
}
List<Integer> leftList=inorderTraversal(root.left);
list.addAll(leftList);
list.add(root.val);
List<Integer> rightList=inorderTraversal(root.right);
list.addAll(rightList);
return list;
}
}
使用栈:
java
一路向左到底,弹栈访问再走右
public void inOrderIterative(TreeNode root) {
if (root == null) return;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
// 一路压入左孩子
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
// 弹出栈顶(当前最左节点)
cur = stack.pop();
System.out.print(cur.val + " ");
// 转向右孩子
cur = cur.right;
}
}
后序遍历:

java
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list=new ArrayList<>();
if(root==null){
return list;
}
List<Integer> leftList=postorderTraversal(root.left);
list.addAll(leftList);
List<Integer> rightList=postorderTraversal(root.right);
list.addAll(rightList);
list.add(root.val);
return list;
}
}
使用栈:

java
双栈倒序最容易,根右左变左右根
public void postOrderIterative(TreeNode root) {
if (root == null) return;
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
stack1.push(root);
while (!stack1.isEmpty()) {
TreeNode node = stack1.pop();
stack2.push(node);
// 先压左,再压右,保证stack2弹出时是右→左→根
if (node.left != null) stack1.push(node.left);
if (node.right != null) stack1.push(node.right);
}
while (!stack2.isEmpty()) {
System.out.print(stack2.pop().val + " ");
}
}
| 遍历类型 | 访问顺序 | 核心特点 | 典型应用场景 |
|---|---|---|---|
| 前序遍历 | 根 → 左 → 右 | 先访问根,再递归左右 | 复制二叉树、打印目录结构 |
| 中序遍历 | 左 → 根 → 右 | 二叉搜索树的有序输出 | 验证 BST、排序输出 |
| 后序遍历 | 左 → 右 → 根 | 先处理子节点,再处理根节点 | 删除二叉树、计算子树属性 |
(2)其他基本操作


求最大深度:

查找元素:
==》左子树查找到就立马返回 不会遍历右子树
4.二叉树相关数据结构题
(1)相同的树


java
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p==null&&q==null){
return true;
}else if((p!=null&&q==null)||(p==null&&q!=null)){
return false;
}else{
if(p.val!=q.val){
return false;
}else{
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
}
}
}
(2)另一棵树的子树


思路:先判断两个树是否相同 若不同 递归拿root的左右子树依次和subRoot进行相同与否的比较
java
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
// 主树空了 → 找不到
if (root == null) return false;
return isSameTree(root, subRoot) // 1. 当前两棵树一模一样
|| isSubtree(root.left, subRoot) // 2. 在左子树里
|| isSubtree(root.right, subRoot); // 3. 在右子树里
}
//判断两棵树是否完全相同
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) return true;
if (p == null || q == null) return false;
if (p.val != q.val) return false;
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
}
(3)翻转二叉树

java
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
}
}
(4)对称二叉树

java
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) return true;
return isMirror(root.left, root.right);
}
// 判断 a 和 b 是否镜像对称
private boolean isMirror(TreeNode a, TreeNode b) {
// 都空 → 对称
if (a == null && b == null) return true;
// 一个空一个不空 → 不对称
if (a == null || b == null) return false;
// 核心:
// 1. 值相等
// 2. a的左 与 b的右 对称
// 3. a的右 与 b的左 对称
return a.val == b.val
&& isMirror(a.left, b.right)
&& isMirror(a.right, b.left);
}
}
(5)平衡二叉树


java
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
// 1. 看当前节点左右高度差是否 ≤1
int leftH = getHeight(root.left);
int rightH = getHeight(root.right);
if (Math.abs(leftH - rightH) > 1) {
return false;
}
// 2. 递归看左、右子树是否也平衡
return isBalanced(root.left) && isBalanced(root.right);
}
// 求树高度
private int getHeight(TreeNode root) {
if (root == null) return 0;
int left = getHeight(root.left);
int right = getHeight(root.right);
return Math.max(left, right) + 1;
}
}
(6)二叉树的遍历
根据带 # 的前序遍历字符串 → 构建二叉树 → 中序遍历输出


java
import java.util.Scanner;
public class Main {
// 全局下标,用来遍历字符串,记录当前走到第几个字符
static int i;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 循环读取输入(OJ 标准写法)
while (in.hasNext()) {
String s = in.next();
i = 0; // 每次建树前,下标重置为 0
Node root = build(s); // 根据带 # 的前序串构建二叉树
printTree(root); // 中序遍历输出
System.out.println(); // 换行
}
}
/**
* 根据带 # 的前序遍历字符串,递归构建二叉树
* 规则:根 → 左 → 右
* # 表示空节点
*/
public static Node build(String s) {
Node root = null;
// 当前字符不是 #,说明是有效节点
if (s.charAt(i) != '#') {
root = new Node(s.charAt(i)); // 创建当前节点
i++; // 下标后移
root.left = build(s); // 递归构建左子树
root.right = build(s); // 递归构建右子树
} else {
// 遇到 #,表示空节点,只移动下标,返回 null
i++;
}
return root;
}
/**
* 中序遍历:左 → 根 → 右
*/
public static void printTree(Node root) {
if (root == null) {
return;
}
printTree(root.left); // 先遍历左子树
System.out.printf("%c ", root.value); // 访问根节点
printTree(root.right); // 最后遍历右子树
}
}
/**
* 二叉树节点类
*/
class Node {
Node left; // 左孩子
Node right; // 右孩子
char value; // 节点值
public Node(char value) {
this.value = value;
}
}
(7)二叉树的层序遍历=》队列记录 注意应用size
a.层序遍历


java
public List<List<Integer>> levelOrder2(TreeNode root) {
// 最终结果:按层存储的二维列表
List<List<Integer>> ret = new ArrayList<>();
// 空树直接返回空列表
if (root == null) {
return ret;
}
// 队列:存储待遍历的节点,用LinkedList实现Queue接口
Queue<TreeNode> queue = new LinkedList<>();
// 根节点入队,作为遍历起点
queue.offer(root);
// 外层循环:控制遍历的层数,队列不为空说明还有层未处理
while (!queue.isEmpty()) {
// 当前层的节点列表
List<Integer> curRow = new ArrayList<>();
// 关键:记录当前层的节点总数(队列此时的大小就是当前层的节点数)
int size = queue.size();
// 内层循环:遍历当前层的所有节点
while (size != 0) {
// 出队当前层的节点
TreeNode cur = queue.poll();
// 将节点值加入当前层列表
curRow.add(cur.val);
// 左孩子非空,入队(下一层的节点)
if (cur.left != null) {
queue.offer(cur.left);
}
// 右孩子非空,入队(下一层的节点)
if (cur.right != null) {
queue.offer(cur.right);
}
// 当前层节点数减1,直到遍历完当前层所有节点
size--;
}
// 将当前层的列表加入最终结果
ret.add(curRow);
}
return ret;
}
b.自底向上的层序遍历

java
import java.util.*;
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
// 最终结果:自底向上的层序遍历
List<List<Integer>> result = new ArrayList<>();
if (root == null) {
return result;
}
// 队列用于层序遍历(BFS)
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 cur = queue.poll();
currentLevel.add(cur.val);
// 左右孩子入队(下一层的节点)
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
// 将当前层加入结果(此时顺序是「根→叶」)
result.add(currentLevel);
}
// 反转结果,得到「叶→根」的自底向上顺序
Collections.reverse(result);
return result;
}
}
c.二叉树右视图
=》每一层的最后一个节点
java
class Solution {
public List<Integer> rightSideView(TreeNode root) {
//存放结果集
List<Integer> res = new ArrayList<>();
if (root == null) return res;
//利用队列
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
// 当前层有多少个节点
int size = q.size();
// 遍历这一层
for (int i = 0; i < size; i++) {
TreeNode cur = q.poll();
// ✨ 关键:只把每层最后一个加入结果
if (i == size - 1) {
res.add(cur.val);
}
// 正常层序入队
if (cur.left != null) q.offer(cur.left);
if (cur.right != null) q.offer(cur.right);
}
}
return res;
}
}
d.完全二叉树

java
class Solution {
public boolean isCompleteTree(TreeNode root) {
// 空树是完全二叉树
if (root == null) {
return true;
}
// 队列用于层序遍历
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
if (cur != null) {
queue.offer(cur.left);
queue.offer(cur.right);
} else {
break;
}
}
// 遍历完所有节点 判断剩余队列里有空 → 是完全二叉树
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
if (cur != null) {
return false; // 只要发现一个不是null,直接false
}
}
return true;
}
}
(8)公共最近祖先




java
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return null;
}
if (root == p || root == q) {
return root;
}
TreeNode leftRet = lowestCommonAncestor(root.left, p, q);
TreeNode rightRet = lowestCommonAncestor(root.right, p, q);
if (leftRet != null && rightRet != null) {
return root;
} else if (leftRet != null) {
return leftRet;
} else {
return rightRet;
}
}
}
或者:

获取路径:

(9)前序中序遍历序列构造二叉树


java
class Solution {
//前序遍历的索引位置
public int preIndex=0;
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTreeChild(preorder,inorder,0,inorder.length-1);
}
//在中序遍历序列中找root
public int findRoot(int[] inorder,int inbegin,int inend,int key){
for(int i=inbegin;i<=inend;i++){
if(inorder[i]==key){
return i;
}
}
return -1;
}
public TreeNode buildTreeChild(int[] preorder, int[] inorder,int inbegin,int inend) {
//没有子树
if(inbegin>inend){
return null;
}
//从先序遍历确定根节点
TreeNode root=new TreeNode(preorder[preIndex]);
//在中序遍历序列中找root
int rootIndex= findRoot(inorder,inbegin,inend,preorder[preIndex]);
//指针后移
preIndex++;
root.left=buildTreeChild(preorder,inorder,inbegin,rootIndex-1);
root.right=buildTreeChild(preorder,inorder,rootIndex+1,inend);
return root;
}
}
(10)中序后序遍历序列构造二叉树


java
class Solution {
// 后序遍历的指针,初始指向最后一个元素(根节点)
int postIndex;
public TreeNode buildTree(int[] inorder, int[] postorder) {
postIndex=postorder.length-1;
return buildTreChild(inorder,postorder,0,inorder.length-1);
}
//中序遍历中找根节点
public int findval(int[] inorder,int inbegin,int inend,int key){
for(int i=inbegin;i<=inend;i++){
if(inorder[i]==key){
return i;
}
}
return -1;
}
public TreeNode buildTreChild(int[] inorder, int[] postorder,int inbegin,int inend) {
if(inbegin>inend){
return null;
}
// 取后序当前指针的元素,作为根节点
int rootVal = postorder[postIndex];
TreeNode root = new TreeNode(rootVal);
if(root==null){
return null;
}
//接收中序遍历中根节点的索引位置
int rootIndex =findval(inorder,inbegin,inend,rootVal);
//指针前移
postIndex--;
//先右子树 再左子树 不能反序
root.right=buildTreChild(inorder,postorder,rootIndex+1,inend);
root.left=buildTreChild(inorder,postorder,inbegin,rootIndex-1);
return root;
}
}
(11)二叉树创建字符串


- 左子树处理 :
if (root.left != null):有左孩子就加括号递归。else if (root.right != null):如果没左孩子但有右孩子,必须加个空括号(),这样结果才是1()这种格式。
- 右子树处理 :
- 只要有右孩子,就加括号递归。如果没有,就不处理。
| 情况 | 处理规则 | 示例 |
|---|---|---|
| 左子树非空 | 必须加括号()包裹递归结果 |
1(2) |
| 左子树空、右子树非空 | 必须补空括号()占位 |
1()(3) |
| 左子树空、右子树空 | 不做任何操作,省略空括号 | 4(无括号) |
| 右子树非空 | 必须加括号()包裹递归结果 |
1(3) |
| 右子树空 | 不做任何操作,省略空括号 | 1(2) |
java
class Solution {
public String tree2str(TreeNode root) {
StringBuilder sb = new StringBuilder();
tree2strChild(sb, root);
return sb.toString();
}
public void tree2strChild(StringBuilder sb,TreeNode root){
if(root==null){
return;
}
// 先拼自己!!!
sb.append(root.val);
// 处理左子树
if(root.left!=null){
sb.append("(");
tree2strChild(sb, root.left);
sb.append(")");
}else{
if(root.right!=null){
sb.append("()");
}else{
return;
}
}
// 处理右子树
if (root.right != null) {
sb.append("(");
tree2strChild(sb, root.right);
sb.append(")");
}
}
}
5.二叉搜索树
其中序遍历是有序的。
查找
java
public TreeNode search(TreeNode root, int key) {
if (root == null) {
return null;
}
TreeNode cur = root;
while (cur != null) {
if (cur.val < key) {
cur = cur.right;
} else if (cur.val == key) {
return cur;
} else {
cur = cur.left;
}
}
return null;
}
最好情况O(logN):树为满二叉树 / 完全二叉树,树高为logN,最多遍历logN次
最坏情况O(N):树退化为单分支链表,树高为N,最多遍历N次
插入

java
public boolean insert(int val) {
if (root == null) {
root = new TreeNode(val);
return true;
}
TreeNode node = new TreeNode(val);
TreeNode cur = root;
TreeNode parent = null;
while (cur != null) {
if (cur.val < val) {
parent = cur;
cur = cur.right;
} else if (cur.val > val) {
parent = cur;
cur = cur.left;
} else {
return false;
}
}
if (parent.val > val) {
parent.left = node;
} else {
parent.right = node;
}
return true;
}
删除








java
public boolean delete(int key) {
// 1. 查找待删节点cur及其父节点parent
TreeNode parent = null;
TreeNode cur = root;
while (cur != null) {
if (cur.val == key) {
break;
}
parent = cur;
if (cur.val < key) {
cur = cur.right;
} else {
cur = cur.left;
}
}
if (cur == null) {
return false;
}
// 2. 调用removeNode删除节点
removeNode(parent, cur);
return true;
}
private void removeNode(TreeNode parent, TreeNode cur) {
// 情况1:左孩子为空(含叶子节点)
if (cur.left == null) {
if (cur == root) {
root = cur.right;
} else if (cur == parent.left) {
parent.left = cur.right;
} else {
parent.right = cur.right;
}
}
// 情况2:右孩子为空
else if (cur.right == null) {
if (cur == root) {
root = cur.left;
} else if (cur == parent.left) {
parent.left = cur.left;
} else {
parent.right = cur.left;
}
}
// 情况3:左右孩子都存在(核心难点)
else {
TreeNode target = cur.right;
TreeNode targetParent = cur;
while (target.left != null) {
targetParent = target;
target = target.left;
}
cur.val = target.val;
if (target == targetParent.left) {
targetParent.left = target.right;
} else {
targetParent.right = target.right;
}
}
}
}
