力扣例题----二叉树

文章目录

  • 1. 100.相同的树
  • 2. 572. 另一颗树的子树
  • 3. 266.翻转二叉树
  • 4. LCR 175.计算二叉树的深度
  • 5. 110.平衡二叉树
  • 6. 101. 对称二叉树
  • 7. 牛客题目:KY11 二叉树遍历
  • 8. 102.二叉树的层序遍历
  • 9. 236.二叉树的最近公共祖先
  • 10. 105.根据前序和中序构造一棵二叉树
  • 11. 106.根据中序和后序构造一棵二叉树
  • 12. 606.根据二叉树创建字符串
  • 13. 144.二叉树的前序遍历
  • 14. 94.二叉树的中序遍历
  • 15. 145.二叉树的后序遍历

1. 100.相同的树

【题目链接】:
100.相同的树
【题目描述】:
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
在这里插入图片描述
在这里插入图片描述

【解题过程】:
两个二叉树相同,当且仅当两个二叉树的结构完全相同,且所有对应节点的值相同。让两个指针一开始先指向两棵二叉树的根节点,然后同步移动两根指针来同步遍历这两棵树,判断对应位置是否相等。
在这里插入图片描述

算法实现步骤:

  1. 如果两个二叉树中有且只有一个为空,则两个二叉树结构不相同,两棵树一定不相同。
  2. 如果两个二叉树都为空,则两个二叉树相同。
  3. 如果两个二叉树都不为空,那么首先判断它们的根节点的值是否相同,若不相同则两个二叉树一定不同
  4. 若相同,再分别判断两个二叉树的左子树是否相同以及右子树是否相同。

【代码】:

 public boolean isSameTree(TreeNode p, TreeNode q) {//结构上不相同if(p == null && q != null || p!= null && q==null) {return false;}//结构上相同,此时p、q节点要么同时为null,要么都不为nullif(p == null && q == null){return true;}//此时,p、q都不为null,判断p、q是否相等if(p.val != q.val) {return false;}//p、q两个根结点相等,然后再判断根节点的左右子树return isSameTree(p.left,q.left) && isSameTree(p.right ,q.right);}

复杂度分析:
【时间复杂度】:O(min⁡(m,n)),其中 m 和 n分别是两个二叉树的节点数。只有当两个二叉树中的对应节点都不为空时才会访问到该节点,因此被访问到的节点数不会超过较小的二叉树的节点数。

2. 572. 另一颗树的子树

【题目链接】:
572. 另一颗树的子树
【题目描述】:
给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
在这里插入图片描述
在这里插入图片描述

【解题过程】:
遍历root 中的每一个节点,判断这个点的子树是否和 subRoot相等。让两个指针一开始先指向root的节点和 subRoot的根,然后同步移动两根指针来同步遍历这两棵树,判断对应位置是否相等。

算法步骤:

  1. 首先判断 root和subRoot是否​为空,如果为空说明已越过叶节点,因此返回false 。
  2. 判断两棵树是否相同
  3. 判断root的左子树和subRoot是否相同
  4. 判断root的右子树和subRoot是否相同

【代码]:

 	public boolean isSubtree(TreeNode root, TreeNode subRoot) {if (root == null || subRoot == null) {return false;}// 1.首先判断两棵树是否相同if (isSameTree(root, subRoot)) {return true;}// 2.判断root的左子树和subroot是否相同if (isSubtree(root.left, subRoot)) {return true;}// 3.判断root的右子树和subroot是否相同if (isSubtree(root.right, subRoot)) {return true;}// 4.返回结果return false;}public boolean isSameTree(TreeNode p, TreeNode q) {if (p == null && q != null || p != null && q == null) {return false;}// 此时p、q节点要么同时为null,要么都不为nullif (p == null && q == null) {return true;}// 此时,p、q都不为null,判断p、q是否相等if (p.val != q.val) {return false;}// p、q两个根结点相等,然后再判断根节点的左右子树return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);}

时间复杂度:假设root含有m个节点,subRoot含有n个节点,对于每一个 root上的点,都需要做一次遍历来和 subRoot匹配,匹配一次的时间代价是 O(n),那么总的时间代价就是 O(m * n)。故时间复杂度为 O(m * n).

3. 266.翻转二叉树

【题目链接】:
266.翻转二叉树
【题目描述】:
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
在这里插入图片描述

【解题过程】:
我们从根节点开始,递归地对树进行遍历,并从叶子节点先开始翻转。如果遍历到的节点 root ,就把root节点的左右两棵子树的位置进行交换,直到所有的节点都进行遍历,即可完成以 root 为根节点的整棵子树的翻转。
算法步骤:

  1. 首先判断 root是否​为空,如果为空说明已越过叶节点,因此返回null。
  2. 判断左子树和右子树是否都为空,都为空(叶子节点)时不进行交换直接返回root。
  3. 当左右子树都不为空,将根节点的两个引用进行交换
  4. 采用递归的方式翻转根节点的左子树
  5. 采用递归的方式翻转根节点的右子树

【代码】:

public TreeNode invertTree(TreeNode root) {if(root == null){return null;}//左子树和右子树都为空if(root.left == null && root.right == null) {return root;}//左子树和右子树不为null,将左子树和右子树进行翻转TreeNode tmp = root.left;root.left = root.right;root.right = tmp;//将左子树翻转invertTree(root.left);//将右子树翻转invertTree(root.right);return root;}

时间复杂度:O(N),其中 N 为二叉树节点的数目。我们会遍历二叉树中的每一个节点,对每个节点而言,我们在常数时间内交换其两棵子树。

4. LCR 175.计算二叉树的深度

【题目链接】:
LCR 175.计算二叉树的深度
【题目描述】:
某公司架构以二叉树形式记录,请返回该公司的层级数。
在这里插入图片描述

【解题过程】:

关键点: 二叉树的深度和二叉左(右)子树的深度之间的关系。二叉树的深度等于左子树的深度与右子树的深度中的最大值 +1

在这里插入图片描述
在这里插入图片描述

算法步骤:

  1. 首先判断 root是否​为空,如果root为空说明已越过叶节点,因此返回深度0 。
  2. 计算根节点 root​ 的 左子树的深度。
  3. 计算根节点 root​ 的 右子树的深度。
  4. 返回二叉树的深度 ,二叉树的深度等于左子树的深度与右子树的深度中的最大值 +1。

【代码】:

public int height(TreeNode root) {if(root == null) {return 0;}//计算左子树的深度int leftNode = height(root.left);//计算右子树的深度int rightNode = height(root.right); //二叉树的深度等于左子树的深度与右子树的深度中的最大值 +1 return leftNode > rightNode ? leftNode + 1 :rightNode + 1;}

时间复杂度 O(N) : N 为树的节点数量,计算树的深度需要遍历所有节点。

5. 110.平衡二叉树

【题目链接】:
110.平衡二叉树
【题目描述】:
给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

【解题过程】:
二叉树的根节点的左右子树的高度差的绝对值不超过 1,并且二叉树的左子树和右子树也是平衡的,则说明该二叉树是平衡二叉树。根据定义,一棵二叉树是平衡二叉树,当且仅当其所有子树也都是平衡二叉树,因此可以使用递归的方式判断二叉树是不是平衡二叉树。
在这里插入图片描述

算法步骤:

  1. 首先判断 root是否​为空,如果root为空说明已越过叶节点,或者二叉树为空树,是平衡的 。
  2. 计算根节点 root​ 的 左子树的深度。
  3. 计算根节点 root​ 的 右子树的深度。
  4. 判断二叉树的根节点的左右子树的高度差的绝对值是否不超过 1,并且二叉树的左子树和右子树也是平衡的,如果都满足则说明该二叉树是平衡二叉树

【代码】:

	public boolean isBalanced(TreeNode root) {//如果root为空说明已越过叶节点,或者二叉树为空树,是平衡的if (root == null) {return true;}// 获取左右子树的高度int left = height(root.left);int right = height(root.right);//二叉树的根节点的左右子树的高度差的绝对值不超过 1,并且二叉树的左子树和右子树也是平衡的,此时二叉树是平衡的return Math.abs(left - right) <= 1 && isBalanced(root.left) && isBalanced(root.right);}public int height(TreeNode root) {if (root == null) {return 0;}int leftHeight = height(root.left);int rightHeight = height(root.right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}

复杂度分析:
时间复杂度:O(n^2),其中 n是二叉树中的节点个数。最坏情况下,二叉树是满二叉树,需要遍历二叉树中的所有节点,每个节点求高度时的时间复杂度为O(n),所以总的时间复杂度是 O(n^2)。

【代码优化】:
上述由于是自顶向下递归,因此对于同一个节点,函数 height会被重复调用,导致时间复杂度较高。如果使用自底向上的做法,则对于每个节点,函数 heightt 只会被调用一次。

优化思想:减少重复高度的计算,一边求高度一边判断是否平衡的问题,

  1. 先递归地判断其左右子树是否平衡
  2. 再判断以当前节点为根的子树是否平衡。如果一棵子树是平衡的,则返回其高度(高度一定是非负整数),否则返回 −1(不平衡)。

如果左右子树都是平衡的,并且它们之间的高度差小于或等于1,则计算并返回最大高度加1。而如果不平衡,那么就会返回-1。这种方法利用哨兵值(即标志值或特殊值)通过递归向上传播不平衡-1状态。如果存在一棵子树不平衡,则整个二叉树一定不平衡。

    public boolean isBalanced(TreeNode root) {return height(root) >= 0;}public int height(TreeNode root) {//如果root为空说明已越过叶节点,或者二叉树为空树,是平衡的if (root == null) {return 0;}int leftHeight = height(root.left);//如果左子树的高度是-1,那么左子树不平衡if (leftHeight < 0) {return -1;}int rightHeight = height(root.right);//如果右子树的高度是-1,那么右子树不平衡if (rightHeight < 0) {return -1;}//此地左右子树都是平衡的,需要判断当前节点的二叉树是否平衡,//如果当前节点的左右子树的高度差的绝对值不超过 1,那么该树平衡,返回该二叉树的高度if (Math.abs(leftHeight - rightHeight) <= 1) {return Math.max(leftHeight, rightHeight) + 1;} else {//如果超过 1,那么该二叉树是不平衡的,返回-1return -1;}}

【复杂度分析】:
时间复杂度:O(n),其中 n 是二叉树中的节点个数。使用自底向上的递归,每个节点的计算高度和判断是否平衡都只需要处理一次,最坏情况下需要遍历二叉树中的所有节点,因此时间复杂度是 O(n)。

6. 101. 对称二叉树

【题目链接】:
101. 对称二叉树
【题目描述】:
给你一个二叉树的根节点 root , 检查它是否轴对称。在这里插入图片描述
在这里插入图片描述

【解题过程】:
首先,如果一个树的左子树与右子树镜像对称,那么这个树是对称的。

如果同时满足下面的条件,两个树互为镜像:

  1. 它们的两个根结点具有相同的值
  2. 每个树的右子树都与另一个树的左子树镜像对称

在这里插入图片描述

在这里插入图片描述
我们可以实现这样一个递归函数,通过「同步移动」两个指针的方法来遍历这棵树,treeLeft指向左子树,treeRight指向右子树,然后进行判断:

  1. 当左子树存在右子树不存在,或者当右子树存在左子树不存在时不是对称二叉树
  2. 判断左右子树是否存在,如果左右子树都不存在那么是对称的
  3. 如果左右子树都存在,首先判断根节点的值是否相同,如果相等再判断左右子树是否对称。由于对称,需要再判断左子树的左子树和右子树的右子树,左子树的右子树和右子树的左子树是否相同

【代码】:

    public boolean isSymmetric(TreeNode root) {//是一颗空的二叉树if(root == null) {return true;}return isSymmetricChild(root.left,root.right);}public boolean isSymmetricChild(TreeNode treeLeft,TreeNode treeRight) {//当左子树存在右子树不存在,或者当右子树存在左子树不存在时不是对称二叉树if(treeLeft == null && treeRight != null || treeLeft != null && treeRight == null){return false;}//此时,要么左右子树都不存在if(treeLeft == null && treeRight == null) {return true;}//要么左右子树都存在,首先判断根节点的值是否相同if(treeLeft.val != treeRight.val) {return false;}//此时,左右子树根节点的值相同,由于对称,需要再判断左子树的左子树和右子树的右子树,左子树的右子树和右子树的左子树是否相同return isSymmetricChild(treeLeft.left,treeRight.right) && isSymmetricChild(treeLeft.right,treeRight.left);}

【复杂度分析】:
时间复杂度:假设二叉树一共 n 个节点。这里遍历了这棵树,时间复杂度为 O(n)。

7. 牛客题目:KY11 二叉树遍历

【题目链接】:
KY11 二叉树遍历
【题目描述】:
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。在这里插入图片描述

【解题过程】:
在这里插入图片描述
根据先序遍历字符串构建二叉树:

  1. 遍历字符串,使用递归的方式构建二叉树,每次取出字符串中的一个字符,如果是#表示空树,则返回null,否则创建一个新的节点,并递归构建其左子树和右子树。
  2. 中序遍历顺序:中序遍历(Inorder Traversal)——根的左子树—>根节点—>根的右子树。 使用递归的方式进行中序遍历,先遍历左子树,然后将当前节点的值添加到结果字符串中,最后遍历右子树。

【代码】:

public class Main {static class TreeNode {public char val;public TreeNode left;//左孩子的引用public TreeNode right;//右孩子的引用public TreeNode(char val) {this.val = val;}}public static int index = 0; //作为字符串遍历时的下标public static TreeNode createTree(String str){TreeNode root = null;if(str.charAt(index) != '#'){//创建一个新节点root = new TreeNode(str.charAt(index));index++;//构建左子树root.left = createTree(str);//构建右子树root.right = createTree(str);}else{index++;}//当字符是#表示空树,返回null,不是#时会创建一个新的节点,返回的是该节点return root;}public static void inOrder(TreeNode root) {if(root == null) {return;}//先遍历左子树inOrder(root.left);//将当前节点的值添加到结果字符串中System.out.print(root.val+" ");//最后遍历右子树inOrder(root.right);}public static void main(String[] args) {Scanner in = new Scanner(System.in);String str = in.nextLine();//读取先序遍历字符串TreeNode root = createTree(str);//构建二叉树inOrder(root);//中序遍历}
}

我们并不用考虑,index 是否会越界的情况,因为我们根本没有用 index 来控制循环次数,而是一边前序遍历,一边构建整棵树。

我们设置 index 为静态变量,那么它只适用于一个测试用例,如果有多个测试用例,那么index已经保留了上一个测试用例的值,可能无法正常运行(在力扣上会报错)。所以最好的,我们要把 static 去掉,这样即使有多个测试用例,也可以得到正确结果。

public class Main {static class TreeNode {public char val;public TreeNode left;//左孩子的引用public TreeNode right;//右孩子的引用public TreeNode(char val) {this.val = val;}}public static int index = 0;public static TreeNode createTree(String str) {index = 0;//每调用一个实例,就会重置index下标return createTreeTmp(str);}private static TreeNode createTreeTmp(String str) {TreeNode root = null;if (str.charAt(index) != '#') {//创建一个新节点root = new TreeNode(str.charAt(index));index++;//构建左子树root.left = createTreeTmp(str);//构建右子树root.right = createTreeTmp(str);} else {index++;}//当字符是#表示空树,返回null,不是#时会创建一个新的节点,返回的是该节点return root;}public static void inOrder(TreeNode root) {if (root == null) {return;}//先遍历左子树inOrder(root.left);//将当前节点的值添加到结果字符串中System.out.print(root.val + " ");//最后遍历右子树inOrder(root.right);}public static void main(String[] args) {Scanner in = new Scanner(System.in);String str = in.nextLine();//读取先序遍历字符串TreeNode root = createTree(str);//构建二叉树inOrder(root);//中序遍历}
}

可以使用辅助方法,在每次调用创建二叉树时,提前将index重置为0,在调用辅助方法构建二叉树,即可通过多个测试用例。

8. 102.二叉树的层序遍历

【题目链接】:
102.二叉树的层序遍历
【题目描述】:
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
在这里插入图片描述
在这里插入图片描述

【解题过程】:层序遍历:从上到下、从左到右依次进行遍历
借助队列实现:

  1. 首先根元素入队
  2. 当队列不为空的时候,将队列中的元素出队进行打印,出队的同时将出队元素的左右子树的根节点(不为空)进行入队
  3. 然后进入下一次迭代,直到队列中没有元素
    在这里插入图片描述
    在这里插入图片描述

【代码】:

	 //层序遍历(借助队列)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);}}}

使用二元组接受层序遍历的结果:
在这里插入图片描述

  1. 首先创建一个结果列表result来存储层序遍历的结果。
  2. 创建一个队列queue并将根节点入队。
  3. 我们使用一个循环来处理每一层的节点。在循环中,我们首先获取当前层的节点数size,然后创建一个列表tmp 来存储当前层的节点值。接着,我们从队列中取出size个节点,将它们的值加入tmp 列表,并将它们的非空子节点加入队列。
  4. 完成当前层的处理后,我们将tmp列表加入ret列表,然后进行下一层迭代。
  5. 当队列为空时,我们完成了二叉树的层序遍历。
public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> ret = new ArrayList<>();//二叉树为空,不打印if (root == null) {return ret;}//创建辅助队列Queue<TreeNode> queue = new LinkedList<>();//将头节点入队queue.offer(root);//迭代打印,直到队列为空while (!queue.isEmpty()) {//获取一行元素的个数,也就是求一下当前队列的大小  int size = queue.size();//将每一行的元素放在一个集合中List<Integer> tmp = new ArrayList<>();//当size为0时,说明一层元素都出队了,迭代遍历下一层元素while (size != 0) {TreeNode cur = queue.poll();tmp.add(cur.val);size--;if (cur.left != null) {queue.offer(cur.left);}if (cur.right != null) {queue.offer(cur.right);}}//将存储一行元素的集合存入最终的二元组中ret.add(tmp);}return ret;}

时间复杂度:每个点进队出队各一次,故渐进时间复杂度为 O(n)。

9. 236.二叉树的最近公共祖先

【题目链接】:
236.二叉树的最近公共祖先
【题目描述】:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

在这里插入图片描述
在这里插入图片描述

【解题过程】:
解题方法一:

p、q的分布共有以下几种情况:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
算法思想:遍历二叉树,遇到p或者q就返回
在这里插入图片描述在这里插入图片描述

【代码】:

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null) {return null;}//第1种情况:root是p或者q中的一个,那么结果为rootif(p == root || q == root) {return root;}TreeNode leftTree = lowestCommonAncestor(root.left,p,q);TreeNode rightTree = lowestCommonAncestor(root.right,p,q);//第2种情况:左右子树都不为空,结果为rootif(leftTree != null && rightTree != null) {return root;//第3种情况:右子树的结果为空,左子树不为空,结果为左子树这个不为空的值}else if(leftTree != null) {return leftTree;//第4种情况:左子树的结果为空,右子树不为空,结果为右子树这个不为空的值}else{return rightTree;}}

首先检查当前节点是否等于节点p或节点q。如果是,那么当前节点就是最近公共祖先。否则,我们分别在左子树和右子树中递归寻找最近公共祖先。如果左子树和右子树都返回非空节点,那么说明节点p和节点q分别在当前节点的左子树和右子树中,当前节点就是最近公共祖先。如果只有左子树返回非空节点,那么说明节点p和节点q都在左子树中,返回左子树的结果作为最近公共祖先。如果只有右子树返回非空节点,那么说明节点p和节点q都在右子树中,返回右子树的结果作为最近公共祖先。

解题方法二(借用辅助栈):
使用求链表的交点的方法:

  1. 将节点p和节点q搜索路径分别存储到两个栈中

在这里插入图片描述
2. 让较长的栈先弹出两个栈的长度差个元素
在这里插入图片描述

  1. 同时比较两个栈的栈顶元素,如果不相等则比较下一对栈顶元素,如果相等那么就是最近公共祖先(由上向下遍历二叉树,一定是最近公共祖先)。
    在这里插入图片描述
    在这里插入图片描述
    该方法难点:如何存储,从根节点到节点p或者节点q的所有节点
    在这里插入图片描述
    就将不是该路径上的节点出栈,直接stack.pop();
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if (root == null) {return null;}// 将节点p的搜索路径存放在stackP栈中Stack<TreeNode> stackP = new Stack<>();getPath(root, p, stackP);// 将节点q的搜索路径存放在stackQ栈中Stack<TreeNode> stackQ = new Stack<>();getPath(root, q, stackQ);// 让较长的栈先弹出两个栈的长度差个元素int sizeP = stackP.size();int sizeQ = stackQ.size();if (sizeP > sizeQ) {int size = sizeP - sizeQ;while (size != 0) {stackP.pop();size--;}} else {int size = sizeQ - sizeP;while (size != 0) {stackQ.pop();size--;}}// 同时比较两个栈的栈顶元素while (!stackP.isEmpty() && !stackQ.isEmpty()) {if (stackP.peek() == stackQ.peek()) {return stackP.peek();} else {// 如果不相等则比较下一对栈顶元素stackP.pop();stackQ.pop();}}return null;}public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {if (root == null || node == null) {return false;}// 将根节点放入栈中stack.push(root);if (root == node) {return true;}// 判断左子树boolean flg1 = getPath(root.left, node, stack);if (flg1 == true) {return true;}// 判断右子树boolean flg2 = getPath(root.right, node, stack);if (flg2 == true) {return true;}// 代码到这,说明左子树和右子树都没有node节点,将根节点弹出stack.pop();return false;}

10. 105.根据前序和中序构造一棵二叉树

【题目链接】:
105.根据前序和中序构造一棵二叉树
【题目描述】:
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
在这里插入图片描述

【解题过程】:
先序遍历可以确定根结点的位置,在先序遍历中的第一个节点就是根结点。中序遍历可以确定左右子树,根节点的左边就是左子树,右边的就是右子树的结点。

二叉树创建过程:
在这里插入图片描述
算法步骤:

  1. 在先序遍历中找到根节点
  2. 根据先序遍历的根节点,在中序遍历结果中找到该节点的位置
  3. 在中序遍历中根节点的前半部分为左子树,根节点的后半部分为右子树
  4. 迭代上面的过程构建一颗二叉树
    在这里插入图片描述

【代码】:

    public static int index;public TreeNode buildTree(int[] preorder, int[] inorder) {// 先序遍历中根节点的下标index = 0;TreeNode root = buildTreeChild(preorder, inorder, 0, inorder.length - 1);return root;}private TreeNode buildTreeChild(int[] preorder, int[] inorder, int inbegin, int inend) {if (inbegin <= inend) {TreeNode root = new TreeNode(preorder[index]);// 获取中序遍历中根节点的位置int getRoot = getPath(inorder, inbegin, inend, preorder[index]);index++;// 构建左子树root.left = buildTreeChild(preorder, inorder, inbegin, getRoot - 1);// 构建右子树root.right = buildTreeChild(preorder, inorder, getRoot + 1, inend);return root;} else {return null;}}private int getPath(int[] inorder, int inbegin, int inend, int target) {for (int i = inbegin; i <= inend; i++) {if (inorder[i] == target) {return i;}}return -1;}

11. 106.根据中序和后序构造一棵二叉树

【题目链接】:
106.根据中序和后序构造一棵二叉树
【题目描述】:
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
在这里插入图片描述

【解题过程】:
后序遍历可以确定根结点的位置,在后序遍历结果中的最后一个节点就是根结点。中序遍历可以确定左右子树,根节点的左边就是左子树,右边的就是右子树的结点。

在这里插入图片描述
算法步骤:

  1. 在后序遍历的结果中找到根节点
  2. 根据后序遍历的根节点,在中序遍历结果中找到该节点的位置
  3. 在中序遍历中根节点的前半部分为左子树,根节点的后半部分为右子树(先创建右子树,后创建左子树)
  4. 迭代上面的过程构建一颗二叉树

【代码】:

class Solution {//根节点的下标public static int postIndex;public TreeNode buildTree(int[] inorder, int[] postorder) {//后序遍历中第一个根节点的位置postIndex = postorder.length - 1;return buildTreeChild(inorder, 0, inorder.length - 1, postorder);}private TreeNode buildTreeChild(int[] inorder, int inBegin, int inEnd, int[] postorder) {if (inBegin > inEnd) {return null;}TreeNode root = new TreeNode(postorder[postIndex]);// 获取中序遍历中根节点的位置int getRootIn = getRootIn(inorder, inBegin, inEnd, postorder[postIndex]);postIndex--;// 构建右子树root.right = buildTreeChild(inorder, getRootIn + 1, inEnd, postorder);// 构建左子树root.left = buildTreeChild(inorder, inBegin, getRootIn - 1, postorder);// 返回根节点return root;}private int getRootIn(int[] inorder, int inBegin, int inEnd, int target) {for (int i = inBegin; i <= inEnd; i++) {if (inorder[i] == target) {return i;}}return -1;}
}

12. 606.根据二叉树创建字符串

【题目链接】:
606.根据二叉树创建字符串
【题目描述】:
给你二叉树的根节点 root ,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。

空节点使用一对空括号对 “()” 表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
在这里插入图片描述
在这里插入图片描述

【解题过程】:
根据题目描述可以分为以下几种情况:

  1. 从根节点进入,如果根节点不为空打印根节点
  2. 左子树和右子树为空时直接结束此次递归
  3. 左子树不为空,右子树为空:先打印(,打印左子树根节点,再打印)
    在这里插入图片描述
  4. 左子树为空,右子树不为空:先打印()
    在这里插入图片描述

【代码】:

	public String tree2str(TreeNode root) {// 创建一个StringBuilder类型数据存放节点StringBuilder sb = new StringBuilder();tree2strChild(root, sb);// StringBuilder类型数据转换为字符串类型并返回return sb.toString();}public void tree2strChild(TreeNode root, StringBuilder sb) {if (root == null) {return;}// 先打印根节点sb.append(root.val);// 左子树为空if (root.left != null) {sb.append("(");tree2strChild(root.left, sb);sb.append(")");} else {// 左右子树都为空if (root.right == null) {return;// 左右子树都不为空} else {  sb.append("()");}}// 打印右子树if (root.right != null) {sb.append("(");tree2strChild(root.right, sb);sb.append(")");}}
    public String tree2str(TreeNode root) {StringBuilder sb = new StringBuilder();tree2strHelper(root, sb);return sb.toString();}private void tree2strHelper(TreeNode root, StringBuilder sb) {if (root == null) {return;}sb.append(root.val);if (root.left != null || root.right != null) {sb.append("(");tree2strHelper(root.left, sb);sb.append(")");}if (root.right != null) {sb.append("(");tree2strHelper(root.right, sb);sb.append(")");}}

力扣给的官方题解:

    public String tree2str(TreeNode root) {if (root == null) {return "";}if (root.left == null && root.right == null) {return Integer.toString(root.val);}if (root.right == null) {return new StringBuffer().append(root.val).append("(").append(tree2str(root.left)).append(")").toString();}return new StringBuffer().append(root.val).append("(").append(tree2str(root.left)).append(")(").append(tree2str(root.right)).append(")").toString();}

13. 144.二叉树的前序遍历

【题目链接】:
144.二叉树的前序遍历
【题目描述】:
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
在这里插入图片描述
在这里插入图片描述

【解题过程】:
使用辅助栈来进行打印:

  1. 当临时变量遇见根节点不为空时,就将根节点入栈,并打印

  2. 然后将临时节点变为根节点的左子树,循环判断,当临时变量为空时在这里插入图片描述

  3. 将临时节点赋值为栈顶元素的右节点,继续循环判断。

在这里插入图片描述

  1. 当栈为空时,打印完毕

【代码】:

 //二叉树递归先序遍历public List<Integer> preorderTraversal(TreeNode root) {//返回的结果List<Integer> list = new ArrayList<>();//临时变量TreeNode cur = root;//创建一个辅助栈Stack<TreeNode> stack = new Stack<>();while (cur != null || !stack.isEmpty()) {while (cur != null) {stack.push(cur);list.add(cur.val);cur = cur.left;}TreeNode tmp = stack.pop();cur = tmp.right;}return list;}
    //二叉树递归先序遍历public List<Character> preorderTraversal(TreeNode root) {List<Character> list = new ArrayList<>();preorderTraversalChild(root, list);return list;}public void preorderTraversalChild(TreeNode root, List<Character> list) {if (root == null) {return;}list.add(root.value);preorderTraversalChild(root.left, list);preorderTraversalChild(root.right, list);}

14. 94.二叉树的中序遍历

【题目链接】:
94.二叉树的中序遍历
【题目描述】:
给定一个二叉树的根节点 root ,返回 它的 中序 遍历。
在这里插入图片描述

在这里插入图片描述

【解题过程】:
使用辅助栈来进行打印:

  1. 当临时变量遇见根节点不为空时,就将根节点入栈

  2. 然后将临时节点变为根节点的左子树,循环判断,当临时变量为空时

  3. 先将栈顶元素打印,再将临时节点赋值为栈顶元素的右节点,继续循环判断。

在这里插入图片描述

  1. 当栈为空时,打印完毕

【代码】:

	//二叉树非递归中序遍历public List<Integer> inorderTraversal(TreeNode root) {//返回的结果List<Integer> list = new ArrayList<>();//临时变量TreeNode cur = root;//创建一个辅助栈Stack<TreeNode> stack = new Stack<>();while(cur != null || !stack.isEmpty()) {while(cur != null) {stack.push(cur);cur = cur.left;}TreeNode tmp = stack.pop();list.add(tmp.val);cur = tmp.right;}return list;}       
    //二叉树递归中序遍历public List<Character> inorderTraversal(TreeNode root) {List<Character> list = new ArrayList<>();inorderTraversalChild(root, list);return list;}public void inorderTraversalChild(TreeNode root, List<Character> list) {if (root == null) {return;}inorderTraversalChild(root.left, list);list.add(root.value);inorderTraversalChild(root.right, list);}

15. 145.二叉树的后序遍历

【题目链接】:
145.二叉树的后序遍历
【题目描述】:
给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。
在这里插入图片描述
在这里插入图片描述

【解题过程】:
后序遍历的顺序:左-右-根
代码思路:我们维护一个辅助栈来实现后序遍历

  1. 使用一个临时变量,并且临时变量一直向左走,同时将节点入栈,直到遇见空时停止
    在这里插入图片描述

  2. 使用临时变量top指向栈顶元素

  3. 此时,栈顶元素的右节点分为两种情况:

  • 栈顶元素的右节点为空,那么就弹出栈顶元素,并且打印栈顶元素,同时使用临时变量prev标记栈顶元素,防止二次打印
    在这里插入图片描述
    在这里插入图片描述

  • 栈顶元素的右节点 不为空,临时变量cur指向栈顶元素的右节点
    在这里插入图片描述
    在这里插入图片描述

  1. 当右边打印完时(top.right = prev),栈顶元素也要出栈。
    在这里插入图片描述

在这里插入图片描述

【代码】:

    public List<Integer> postorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();//创建辅助栈Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;//存储上一个栈顶节点,用于判断右边的树是否打印完TreeNode prev = null;//当栈为空时,说明所有节点打印完了while(cur != null || !stack.isEmpty()) {//一直向左将节点入栈while(cur != null){stack.push(cur);cur = cur.left;}//cur为空,向左的所有节点全部入栈了//获取栈顶节点,用于判断右子树是否打印完TreeNode top = stack.peek();//当栈顶元素的右节点和上一次打印的节点相等,或者栈顶元素的右子树为空时,将栈顶元素出栈,并进行打印,并使用prev记录此次打印的节点if(top.right == prev || top.right == null) {list.add(top.val);stack.pop();prev = top;}else{//将右子树的节点入栈cur = top.right;}}return list;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/683594.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字

作者推荐 视频算法专题 本文涉及知识点 动态规划汇总 LeetCode:1012. 至少有 1 位重复的数字 给定正整数 n&#xff0c;返回在 [1, n] 范围内具有 至少 1 位 重复数字的正整数的个数。 示例 1&#xff1a; 输入&#xff1a;n 20 输出&#xff1a;1 解释&#xff1a;具有至…

【Python】高级数据类型

&#x1f6a9; WRITE IN FRONT &#x1f6a9; &#x1f50e; 介绍&#xff1a;"謓泽"正在路上朝着"攻城狮"方向"前进四" &#x1f50e;&#x1f3c5; 荣誉&#xff1a;2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…

JavaWeb学习|i18n

学习材料声明 所有知识点都来自互联网&#xff0c;进行总结和梳理&#xff0c;侵权必删。 引用来源&#xff1a;尚硅谷最新版JavaWeb全套教程,java web零基础入门完整版 i18n 国际化&#xff08;Internationalization&#xff09;指的是同一个网站可以支持多种不同的语言&…

代码随想录算法训练营第50天 | 70.爬楼梯(进阶) + 322.零钱兑换 + 279.完全平方数

今日任务 70. 爬楼梯 &#xff08;进阶&#xff09; 322. 零钱兑换 279.完全平方数 70.爬楼梯(进阶) - Easy 题目链接&#xff1a;题目页面 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 思路&a…

前沿技术期刊追踪——以电机控制为例

一、背景 前沿技术期刊追踪是指科研人员、学者或专业人士通过关注和阅读各类顶级科技期刊&#xff0c;了解并跟踪相关领域的最新研究成果和发展动态。以下是一些常见的前沿技术期刊以及追踪方法&#xff1a; 1. **知名科技期刊**&#xff1a; - 自然&#xff08;Nature&#…

片上网络NoC(6)——路由算法

目录 一、概述 二、路由算法的类型 三、避免死锁 四、实现 4.1 源路由实现 4.2 基于节点查找表的路由实现 4.3 组合电路实现 五、总结 一、概述 路由算法&#xff08;routing algorithm&#xff09;&#xff0c;即决定数据包在网络拓扑中从起点到终点路径的算法。路由算…

第十九篇【传奇开心果系列】Python的OpenCV库技术点案例示例:文字识别与OCR

传奇开心果短博文系列 系列短博文目录Python的OpenCV库技术点案例示例系列 短博文目录前言一、OpenCV 文字识别介绍二、图像预处理示例代码三、文字区域检测示例代码四、文字识别示例代码五、文字后处理示例代码六、OpenCV结合Tesseract OCR库实现文字识别示例代码七、OpenCV结…

算法沉淀——栈(leetcode真题剖析)

算法沉淀——栈 01.删除字符串中的所有相邻重复项02.比较含退格的字符串03.基本计算器 II04.字符串解码05.验证栈序列 栈&#xff08;Stack&#xff09;是一种基于先进后出&#xff08;Last In, First Out&#xff0c;LIFO&#xff09;原则的数据结构。栈具有两个主要的操作&am…

【王道数据结构】【chapter5树与二叉树】【P159t12】

设一棵二叉树的结点结构为(LLINK,INFO,RLINK)&#xff0c;ROOT为指向该二叉树根结点的指针&#xff0c;p和q分别为指向该二叉树中任意两个节点的指针&#xff0c;试编写算法ANCESTOR(ROOT,p,q,r)&#xff0c;找到p和q的最近公共祖先结点r #include <iostream> #include &…

re:从0开始的CSS学习之路 9. 盒子水平布局

0. 写在前面 过年也不能停止学习&#xff0c;一停下就难以为继&#xff0c;实属不应 1. 盒子的水平宽度 当一个盒子出现在另一个盒子的内容区时&#xff0c;该盒子的水平宽度“必须”等于父元素内容区的宽度 盒子水平宽度&#xff1a; margin-left border-left padding-lef…

QT 工具栏 状态栏 停靠部件 核心部件

添加/删除工具栏 删除工具栏方法和删除菜单栏方法一样&#xff0c;不过工具栏可以有多个&#xff0c;所以每次右键MainWindow对象&#xff0c;都可以看到添加工具栏的选项。 工具栏添加动作 新添加的QAction对象会在动作编辑器里找到&#xff08;Action Editor&#xff09;&a…

计算机组成原理(1)----主存储器

目录 1.基本半导体元件及原理 2.寻址 1.基本半导体元件及原理 一个主存储器可以分为存储器&#xff0c;MAR&#xff08;地址寄存器&#xff09;和MDR&#xff08;数据寄存器&#xff09;&#xff0c;这三个部件由在时序控制逻辑的控制下工作 其中存储体用来存放二进制数据0和…

[字符串] KMP与字符哈希

KMP 首先&#xff0c;要知道在KMP算法里的 next 数组里&#xff0c;对操作的字符串到底存储了什么。 以当前字符为结尾的子串&#xff0c;真前缀与真后缀相同的最长长度。&#xff08;注意&#xff1a;不是说回文&#xff1b;而且是“真”&#xff0c;也就是说&#xff0c;不…

国产制造,欧美品质:爱可声助听器产品质量获国际认可

随着科技的发展和全球化的推进&#xff0c;越来越多的中国制造产品开始走向世界舞台。其中&#xff0c;爱可声助听器凭借其卓越的产品质量&#xff0c;成为了国产制造的骄傲。 国产制造指的是在中国境内生产的产品&#xff0c;欧美品质则是指产品在设计、生产、质量控制等方面…

基于RBF神经网络的自适应控制器simulink建模与仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1自适应控制器 4.2 RBF神经网络模型 5.完整程序 1.程序功能描述 在simulink中&#xff0c;使用S函数编写基于RBF神经网络的自适应控制器&#xff0c;然后实现基于RBF神经网络的自适应控制…

手撕链表OJ

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

Vue CLI学习笔记

在看任何开源库的源码之前&#xff0c;必须先了解它有哪些功能&#xff0c;这样才能针对性地分模块阅读源码。 Vue CLI 简介 Vue CLI是Vue.js的官方命令行工具&#xff0c;它是一个基于Vue.js进行快速开发的完整系统。 通过Vue CLI&#xff0c;开发者可以快速搭建和开发Vue.js项…

VC++ 绘制折线学习

win32 有三个绘制折线的函数&#xff1b; Polyline&#xff0c;根据给定点数组绘制折线&#xff1b; PolylineTo&#xff0c;除了绘制也更新当前位置&#xff1b; PolyPolyline&#xff0c;绘制多条折线&#xff0c;第一个参数是点数组&#xff0c;第二个参数是一个数组、指…

HTML快速入门教程

HTML&#xff1a;超文本标记语言&#xff08;Hyper Text Markup Language&#xff09;&#xff0c;是通过标签的形式将内容组织起来然后共享到网络之上供其他电脑访问查看。 大家可以思考一下&#xff0c;怎么将自己电脑上的文件或图片共享给其他电脑&#xff1f; 这时候会说通…

.target勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

导言&#xff1a; 网络安全威胁如勒索病毒已经成为企业和个人数据安全的重大挑战之一。.target勒索病毒作为其中的一种&#xff0c;以其高度复杂的加密算法和迅速变化的攻击手法备受关注。本文将深入介绍.target勒索病毒的特点&#xff0c;探讨如何有效地恢复被加密的数据文件…