1.两颗相同的树
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。100. 相同的树 - 力扣(LeetCode)
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if((p==null && q!=null) || (p!=null) && (q==null)){
return false;
}
if(p==null&&q==null){
return true;
}
if(p.val!=q.val){
return false;
}
return (isSameTree(p.left,q.left)&&isSameTree(p.right,q.right));
}
}
题解:先判断根节点的值是否相同,再利用递归分别判断第一颗树与第二课树的左子树是否一一对应,右子树的值也是否一一对应。
2.另一颗树的子树:
给你两棵二叉树 root
和 subRoot
。检验 root
中是否包含和 subRoot
具有相同结构和节点值的子树。如果存在,返回 true
;否则,返回 false
。二叉树 tree
的一棵子树包括 tree
的某个节点和这个节点的所有后代节点。tree
也可以看做它自身的一棵子树。572. 另一棵树的子树 - 力扣(LeetCode)
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null){
return false;
}
if(isSameTree(root,subRoot)){
return true;
}
if(isSubtree(root.left,subRoot)){
return true;
}
if(isSubtree(root.right,subRoot)){
return true;
}
return false;
}
public boolean isSameTree(TreeNode p, TreeNode q) {
if((p==null && q!=null) || (p!=null) && (q==null)){
return false;
}
if(p==null&&q==null){
return true;
}
if(p.val!=q.val){
return false;
}
return (isSameTree(p.left,q.left)&&isSameTree(p.right,q.right));
}
}
题解:从根节点开始,依次往下递归判断子树是否有相同的区段(用到上一题的判断两棵树是否相同)。
3.翻转二叉树:
给你一棵二叉树的根节点 root
,翻转这棵二叉树,并返回其根节点。226. 翻转二叉树 - 力扣(LeetCode)
class Solution {
public TreeNode invertTree(TreeNode root) {
TreeNode tmp;
if(root == null){
return null;
}
tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
题解:同样利用递归的思路,遇到空节点的时候返回,依次往下递归,左子树翻转和右子树翻转。
4.平衡二叉树:
给定一个二叉树,判断它是否是 平衡二叉树 110. 平衡二叉树 - 力扣(LeetCode)
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
return Math.abs(getHeight(root.left)-getHeight(root.right))<=1&&isBalanced(root.left)&&isBalanced(root.right);
}
public int getHeight(TreeNode root){
if(root == null){
return 0;
}
int right = getHeight(root.right);
int left = getHeight(root.left);
return Math.max(right,left) + 1;
}
}
题解:首先在根节点的时候判断左子树和右子树高度差是否大于1,再用递归的思想分别判断左右子树是否同样满足条件。
5.对称二叉树:
给你一个二叉树的根节点 root
, 检查它是否轴对称。101. 对称二叉树 - 力扣(LeetCode)
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null){
return true;
}
return isSymmetricchild(root.left, root.right);
}
public boolean isSymmetricchild(TreeNode rootleft, TreeNode rootright){
// 两个节点都为null,对称
if(rootleft == null && rootright == null){
return true;
}
// 一个为null,一个不为null,不对称
if(rootleft == null || rootright == null){
return false;
}
// 节点值不相等,不对称
if(rootleft.val != rootright.val){
return false;
}
// 递归检查子节点:左节点的左孩子对应右节点的右孩子,左节点的右孩子对应右节点的左孩子
return isSymmetricchild(rootleft.left, rootright.right)
&& isSymmetricchild(rootleft.right, rootright.left);
}
}
6.遍历二叉树:
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static class TreeNode{
public char val;
public TreeNode left;
public TreeNode right;
public TreeNode(char val){
this.val = val;
}
}
public static int i = 0;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextLine()) { // 改为判断是否有下一行字符串
String str = in.nextLine();
if (str.isEmpty()) continue; // 跳过空行
i = 0; // 重置索引,处理多个测试用例
TreeNode root = creatTree(str);
orderTree(root);
System.out.println(); // 每个测试用例输出后换行
}
}
/**
* 根据先序遍历字符串创建二叉树
* 其中'#'表示空节点
*/
public static TreeNode creatTree(String str){
// 检查是否已遍历完所有字符
if (i >= str.length()) {
return null;
}
char ch = str.charAt(i);
// 如果当前字符是'#',表示空节点
if(ch == '#'){
i++; // 移动到下一个字符
return null;
}
// 创建当前节点
TreeNode root = new TreeNode(ch);
i++; // 移动到下一个字符
// 递归创建左子树
root.left = creatTree(str);
// 递归创建右子树
root.right = creatTree(str);
return root;
}
/**
* 中序遍历二叉树并打印节点值
*/
public static void orderTree(TreeNode root){
if(root == null){
return;
}
orderTree(root.left); // 遍历左子树
System.out.print(root.val + " "); // 打印当前节点
orderTree(root.right); // 遍历右子树
}
}
7.二叉树的分层遍历
给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。102. 二叉树的层序遍历 - 力扣(LeetCode)
先脱离这道题目去思考一下,我们可以使用队列(queue)来完成分层遍历
public void levelOrder(TreeNode root){
if(root == null){
return;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode cur = queue.poll();
System.out.print(cur.val+" ");
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
}
-
边界处理:
- 如果根节点
root
为null
,直接返回,避免空指针异常
- 如果根节点
-
队列初始化:
- 使用
Queue
(具体实现为LinkedList
)来辅助层序遍历 - 首先将根节点加入队列作为遍历的起点
- 使用
-
遍历过程:
- 外层循环:只要队列不为空就继续遍历
- 出队一个节点
cur
,并打印其值 - 如果该节点有左子树,将左子节点入队
- 如果该节点有右子树,将右子节点入队
public List<List<Character>> levelOrder2(TreeNode root) {
List<List<Character>> ret = new ArrayList<>();
if(root == null) {
return ret;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);//A
while (!queue.isEmpty()) {
List<Character> curRow = new ArrayList<>();
/*记录每一行的数据个数*/
int size = queue.size();//4
while (size != 0) {
TreeNode cur = queue.poll();
curRow.add(cur.val);
//System.out.print(cur.val + " ");
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
size--;
}
ret.add(curRow);
}
return ret;
}
-
初始化:
- 创建一个二维列表
ret
用于存储最终结果 - 如果根节点
root
为null
,直接返回空列表
- 创建一个二维列表
-
队列应用:
- 使用
Queue
来实现层序遍历,首先将根节点加入队列
- 使用
-
层级遍历:
- 外层循环:只要队列不为空就继续遍历
- 记录当前层的节点数量
size
(这是关键,确保我们只处理当前层的节点) - 内层循环:处理当前层的所有节点(循环
size
次)- 出队一个节点并将其值加入当前层的列表
- 如果该节点有左子树,将左子节点入队
- 如果该节点有右子树,将右子节点入队
- 减少当前层节点计数
-
结果收集:
- 每处理完一层,就将当前层的列表加入结果集
ret
- 最终返回整个结果集
- 每处理完一层,就将当前层的列表加入结果集