目录
1.相同的树
LeetCode 相同的树
思路
检查两棵树是否相同 : 基于递归
1. 判定两树的根节点的值是否相同
2. 递归判定左子树是否相同
3. 递归判定右子树是否相同
只要发现以上任一个不同 return false
反之 return true
代码实现
// 1 相同的树
public boolean isSameTree(TreeNode p , TreeNode q){
// 两颗空树
if(p == null && q == null){
return true;
}
// 一颗空一颗不空
if(p == null && q != null || p != null && q == null){
return false;
}
// 都不空
// 检查根节点是否相同
if(p.val != q.val){
return false;
}
//递归检查子树
boolean isLeftSame = isSameTree(p.left,q.left);
boolean isRightSame = isSameTree(p.right,q.right);
return isLeftSame && isRightSame;
}
递归展开
2.另一棵树的子树
LeetCode
思路
依旧递归
1. 先检查 root 和 subRoot 是不是相同
2.如果不相同
递归的判定 root.left 是否包含subRoot
递归的判定 root.right 是否包含subRoot
代码实现
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if (subRoot == null) return true;
if (root == null) return false;
// 检查当前节点是否匹配子树
if (isSameTree(root, subRoot)) {
return true;
}
// 递归检查左子树和右子树
return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
}
private 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.翻转二叉树
思路
针对这个二叉树进行遍历,访问节点操作就是“交换两个子树”
递归的遍历二叉树,把每个阶段的左右子树都进行交换
代码实现
class Solution {
// 递归的遍历二叉树,把每个阶段的左右子树都进行交换
public TreeNode invertTree(TreeNode root) {
if(root == null){
return null;
}
// 交换左右子树
// 此处的交换 要么先递归后交换 ,要么先交换后递归 不能“中序”
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
4.平衡二叉树
思路
针对树进行遍历
针对每个节点,都计算左右子树的高度并计算高度差取绝对值是否 <= 1
递归的判定左子树是否为平衡二叉树
递归的判定右子树是否为平衡二叉树
代码实现
// 平衡二叉树
public int getHeight(TreeNode root){
if(root == null){
return 0;
}
return Math.max(getHeight(root.left) , getHeight(root.right)) + 1;
}
public boolean isBalanced(TreeNode root){
if(root == null){
// 空树认为是平衡的
return true;
}
if(root.left == null && root.right == null){
// 没有左右子树 平衡
return true;
}
// 计算左右子树的深度
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
// 左右子树高度超过 1 即不平衡
if(leftHeight - rightHeight > 1 || rightHeight - leftHeight > 1){
return false;
}
//递归判断左右子树是否平衡
return isBalanced(root.left) && isBalanced(root.right);
}
递归展开
5.对称二叉树
思路
相当于判定这个树的左右子树(t1 t2)是否为镜像
1.判定根节点的值是否相同 不用返回 false
2.判定 t1.left 和 t2.right 是否为镜像
3.判定 t1.right 和 t2.left 是否为镜像
代码实现
// 对称二叉树
public boolean isMorror(TreeNode t1,TreeNode t2){
if(t1 == null && t2 == null){
// 都空认为镜像
return true;
}
if((t1 == null && t2 != null) || (t1 != null && t2 == null)){
// 一空一不空认为不镜像
return false;
}
if(t1.val != t2.val){
// 值不同认为不镜像
return false;
}
return isMorror(t1.left,t2.left) && isMorror(t1.right,t2.right);
}
public boolean isSymmetric(TreeNode root) {
if(root == null){
return true;
}
return isMorror(root.left,root.right);
}
6.二叉树遍历
思路
读入输入的一串先序遍历字符串,根据此字符串建立一个二叉树
此处题目描述不清,到底是 # 是空格还是空格是空树
先序序列中,包含了空树(空节点)是能够唯一确定二叉树的
过程:
取出节点过程中,当前节点下一个节点即为当前节点左子树的根节点
若遇到了 “#”
再下一个节点,即为当前节点右子树的根节点
若树根左右都构建过了,就往上层返回看上层的右子树是否构建了
代码实现
public class Main{
private static int index = 0;
static class Node{
public char val;
Node left = null;
Node right = null;
public Node (char val){
this.val = val;
}
}
public static Node build(String s){
// 取出当前位置index 的字符
char c = s.charAt(index);
if(c == '#' || c == ' '){
return null;
}
// 不为空,把 c 创建成 Node root就是当前子树的根节点
Node root = new Node(c);
// 构建完一个节点了,再取下一个
index++;
// 递归左子树
root.left = build(s);
// 再 ++ 准备取下一个节点
index++;
// 递归右子树
root.right = build(s);
return root;
}
public static void inOrder(Node root){
if(root == null){
return;
}
inOrder(root.left);
System.out.printf(root.val + " ");
inOrder(root.right);
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 每次遍历都在处理一个用例
while(in.hasNextLine()){
String line = in.nextLine();
// 1.构建初二叉树
// 2.中序遍历
// 确保每次构造之前 index 都为零 避免多个用例相互影响
index = 0;
// 创建
Node root = build(line);
// 中序遍历
inOrder(root);
// 整个中序遍历完成后,再换行
System.out.println();
}
}
}
递归展开
7.二叉树的层序遍历
思路
按先序的方式来遍历这个树
递归时,给递归方法,加上辅助的参数,level 表示当前层数
递归过程中,根据 level 的值,决定当前这个节点要放到哪个 List 中
代码实现
class Solution {
public void levelOrderHelper(TreeNode root,int level,List<List<Integer>> result){
if(root == null){
return;
}
// 当前 level 这一层的 List 是否被创建出来了
if(result.size() == level){
// level 这一层的 List 还没有创建 在此处创建一下
result.add(new ArrayList<>());
}
// 取当前元素,添加到第 level 行中
List<Integer> curRow = result.get(level);
curRow.add(root.val);
// 递归处理左右子树
levelOrderHelper(root.left,level+1,result);
levelOrderHelper(root.right,level+1,result);
}
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if(root == null){
return result;
}
// 创建一个辅助方法
levelOrderHelper(root,0,result);
return result;
}
}
递归展开
8.二叉树的最近公共祖先
思路
从根节点出发,递归的针对每个子树,进行查找 p 和 q 的操作
对于任意一个节点,(1)在根节点中查找 p 和 q
(2)在左子树中查找 p 和 q
(3)在右子树中查找 p 和 q
针对上述三个范围的查找,分别使用 int 变量来表示查找结果,
如果找到p 或 q 认为返回值为1,
没找到 p 或 q 返回 0
(1)+ (2)+ (3)= 0 不是
= 1 当前节点可能是 p 或 q 的祖先
= 2 当前节点一定是 p q 的最近公共祖先
代码实现
// 最近公共祖先
private TreeNode lca = null;
public TreeNode lowestCommonAncestor (TreeNode root, TreeNode p, TreeNode q){
// 用成员变量多组用例可能会使成员变量受到干扰,用前先清空
lca = null;
if(root == null){
return null;
}
if(root == p || root == q){
return root;
}
// 使用一个辅助方法进行递归,完成上述逻辑
find(root,p,q);
return lca;
}
// 通过 find 来在 root 中华查找 p q
// 查找范围 根节点 左子树 右子树
// 找到返回 1 没找到返回 0
public int find(TreeNode root,TreeNode p,TreeNode q){
if(root == null){
return 0;
}
// 针对根节点查找
int mid = (root == p || root == q) ? 1:0;
// 针对左子树
int left = find(root.left,p,q);
// 针对右子树
int right = find(root.right,p,q);
if(mid + left + right == 2){
// 说明 root 就是最近公共祖先
// 最近公共祖先只有一个,但 find 方法是递归过程中找到的
// 如何把这个公共祖先返回到 lowestCommonAncestor 方法中呢
// 定义一个成员变量,用成员变量来接收最终结果
lca = root;
}
// 不是直接返回这个变量的和
// root 中同时找到了 p 和 q 此时 root 就是公共祖先,还算做 1
return mid+left+right > 0 ? 1:0;
}
9.从前序与中序遍历序列构造二叉树
代码实现
class Solution {
private int index = 0;
public TreeNode buildTree(int[] preorder, int[] inorder){
index = 0;
return buildTreeHelper(preorder,inorder,0,inorder.length);
}
public TreeNode buildTreeHelper(int[] preorder,int[] inorder,int inLeft,int inRight){
if(inLeft >= inRight){
// 给定的中序区间时空区间
return null;
}
if(index >= preorder.length){
// 已经遍历完 preorder 数组
return null;
}
// 取出 index 对应的元素 构造出当前的根节点
TreeNode root = new TreeNode(preorder[index]);
index++;
// 找到 root 在中序中的位置
int pos = findPos(inorder,inLeft,inRight,root.val);
// 递归左子树
root.left = buildTreeHelper(preorder,inorder,inLeft,pos);
// 递归右子树
root.right = buildTreeHelper(preorder, inorder, pos + 1, inRight);
return root;
}
public int findPos(int[] inorder,int inLeft,int inRight,int val){
// 循环遍历
for(int i = inLeft; i < inRight; i++){
if(inorder[i] == val){
return i;
}
}
return -1;
}
}
递归展开
10.根据二叉树创建字符串
思路
root = [1,2,3,4] 1(2(4))(3)
1 根 (左子树(右子树)
2 3
4
1
2 3 2()(4) 4 为2的右子树
4 2(4) 4 为 2 的左子树
代码实现
class Solution {
// 递归过程中,把构成的 String 结果拼到 res 里面
private StringBuilder res = new StringBuilder();
public String tree2str(TreeNode root) {
res = new StringBuilder();
if(root == null){
return "";
}
tree2strHelper(root);
// 递归完删掉最外层的括号
res.deleteCharAt(0);
res.deleteCharAt(res.length()-1);
return res.toString();
}
// 辅助递归
public void tree2strHelper(TreeNode root){
if(root == null){
return;
}
// 把当前的根节点进行访问,访问操作为拼接字符串
res.append("(");
res.append(root.val);
tree2strHelper(root.left);
// 递归右子树之前 判定是否左子树为空,右子树非空 加空括号
if(root.left == null && root.right != null){
res.append("()");
}
tree2strHelper(root.right);
res.append(")");
}
}
故事就说到这里 就算你们再好奇 我想说的都已说完了 其余是秘密
-——— 暗恋 DT
😊💗🌟 🦀