👉 目录 👈
两棵树相同,需要注意以下几点:
额外注意的是:若两棵树为空,那么也是相同的。

我们可以按照子问题遍历的思想,当左子树和右子树中全部节点同时满足以上条件,说明两棵树相等。
时间复杂度:O(min(m,n))
- class Solution {
- 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;
- }
- //走到这里,说明两棵树的结构相同且不为空,那判断他们的节点值即可
- //若节点值不相同,则说明两棵树不相同,返回false
- if(p.val != q.val) {
- return false;
- }
- //递归遍历
- //左右两棵子树必须同时满足条件
- //才能说明两棵树才相同
- return isSameTree(p.left,q.left) &&
- isSameTree(p.right,q.right);
- }
- }
一棵树的子树 需要满足:
了解子树概念后,我们可以按照以下思路解决问题:
时间复杂度:O(m*n)
因为最坏的情况下:
每个节点和子树值相同,但判断树是否相同时,总是最后一个节点不相同
- /**
- 时间复杂度为:O(m*n)
- 因为最坏的情况下:
- 每个节点和子树值相同,但判断树是否相同时,总是最后一个节点不相同
- */
- class Solution {
- public boolean isSubtree(TreeNode root, TreeNode subRoot) {
- //如果递归到空,说明没有找到子树,返回false
- if(root == null) {
- return false;
- }
- //判断当前节点的整颗树和子树是否相同即可
- if(isSameTree(root,subRoot)) {
- return true;
- }
- //如果递归遇见了子树 则一路返回true
- if(isSubtree(root.left,subRoot)) return true;
- if(isSubtree(root.right,subRoot)) return true;
- //本次递归没有找见子树 返回false
- return false;
- }
-
- public boolean isSameTree(TreeNode p, TreeNode q) {
- // 1.先判断结构是否是一样的
- if (p != null && q == null || p == null && q != null) {
- return false;
- }
- // 上述if语句 如果没有执行,意味着两个引用 同时为空 或者同时不为空
- 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);
- }
- }
这道题的思路很简单,就是:将每个节点的左右子树进行交换。

我们可以以前序遍历思想遍历所有节点,在访问当前树的根节点时将其左右子树进行交换。
根据解题思想,很清晰的分析出时间复杂度为:O(n)
- class Solution {
- public TreeNode invertTree(TreeNode root) {
- if(root == null) {
- return null;
- }
- //交换左右子树
- swapNode(root);
- invertTree(root.left);
- invertTree(root.right);
- return root;
- }
-
- public void swapNode(TreeNode root) {
- TreeNode tmp = root.left;
- root.left = root.right;
- root.right = tmp;
- }
- }
要判断这颗树是对称的,

- class Solution {
- public boolean isSymmetric(TreeNode root) {
- if(root == null) {
- return true;
- }
- return isSymmetricChild(root.left,root.right);
- }
- public boolean isSymmetricChild(TreeNode leftTree,TreeNode rightTree) {
- //空树对称
- if(leftTree == null && rightTree == null) {
- return true;
- }
- //结构不相同 必定不对称
- if(leftTree != null && rightTree == null ||
- leftTree == null && rightTree != null) {
- return false;
- }
- //节点值不相同 必定不对称
- if(leftTree.val != rightTree.val) {
- return false;
- }
- //左子树的左树和右子树的右树
- //左子树的右树和右子树的左树
- //都要对称
- return isSymmetricChild(leftTree.left,rightTree.right)
- && isSymmetricChild(leftTree.right,rightTree.left);
- }
- }
如果一棵树是平衡二叉树,那么满足:

根据平衡二叉树的概念,我们有以下两种思路解决问题。
- class Solution {
- public boolean isBalanced(TreeNode root) {
- //空树 平衡
- if(root == null) {
- return true;
- }
- //当前节点左子树高度
- int h1 = getHeight(root.left);
- //当前节点右子树高度
- int h2 = getHeight(root.right);
- int h = Math.abs(h1-h2);//高度差
- //不平衡
- if(h >= 2) return false;
- //左子树和右子树同时平衡 说明整棵树平衡
- return isBalanced(root.left) &&
- isBalanced(root.right);
- }
-
- public int getHeight(TreeNode root) {
- if (root == null) {
- return 0;
- }
- int leftHeight = getHeight(root.left);
- int rightHeight = getHeight(root.right);
-
- //树的高度为 左子树高度和右子树高度的最大值+节点自身的1个高度
- return leftHeight > rightHeight
- ? leftHeight + 1
- : rightHeight + 1;
- }
- }
思路一虽然能解决问题,但时间复杂度达到了O(n^2),并且产生了很多次重复的计算,例如:圈中节点的高度,在计算它祖先的高度时就被计算了多次,产生了很多重复的计算。

如果要将时间复杂度改善为O(n),该如何完成呢?
O(n)思路分析:

- class Solution {
- /**
- 时间复杂度:O(n)
- */
- public boolean isBalanced(TreeNode root) {
- if(root == null) {
- return true;
- }
-
- //负数说明该树不平衡
- return getHeight(root) > 0;
- }
-
-
- public int getHeight(TreeNode root) {
- if (root == null) {
- return 0;
- }
- int leftHeight = getHeight(root.left);
- //如若发现返回的是负数,则说明该树一定不平衡,一路返回负数
- if(leftHeight < 0) {
- return -1;
- }
- //如若发现返回的是负数,则说明该树一定不平衡,一路返回负数
- int rightHeight = getHeight(root.right);
- if(rightHeight < 0) {
- return -1;
- }
-
- //如果绝对值<=1 说明当前树平衡,返回高度值
- if(Math.abs(leftHeight - rightHeight) <= 1) {
- return Math.max(leftHeight , rightHeight) + 1;
- }else {
- //否则返回负数
- return -1;
- }
- }
- }
二叉搜索树,又称二叉排序树、二叉查找树,具有以下性质:
以中序遍历递归这棵二叉搜索树,将得到的根节点依次连接(修改其左右指针),得到的就是排好序的双向链表,再将链表首尾相连,得到双向循环链表。

时间复杂度:O(n)
- class Solution {
- //定义一个成员变量 存储上一个节点的地址
- private Node prev;//初始值为null
-
- public Node treeToDoublyList(Node root) {
- if (root == null) {
- return root;
- }
- toJoin(root);
- Node head = root;
- Node tail = root;
- //找头结点
- //转换成双向链表后,链表的头结点就在pRootOfTree的左边
- //头结点的left(前驱)为null
- while (head.left != null) {
- head = head.left;
- }
- //找尾结点
- //转换成双向链表后,链表的尾结点就在pRootOfTree的右边
- //尾结点的right(后继)为null
- while (tail.right != null) {
- tail = tail.right;
- }
- //首尾相连
- head.left = tail;
- tail.right = head;
- return head;
- }
-
- private void toJoin(Node root) {
- if (root == null) return;
- //中序遍历思想递归
- toJoin(root.left);
- //修改前驱,将当前节点的left域更改为前一个节点的地址
- root.left = prev;
- if (prev != null) {
- //当prev不为空时,修改后继
- //将其right域修改为当前节点的地址(prev本就是当前节点的前驱)
- prev.right = root;
- }
- //更新prev
- prev = root;
- toJoin(root.right);
- }
- }
我们知道,如果仅仅根据一个遍历方式的结果是无法构建出一棵二叉树的,必须有两个遍历方式且必须包含中序遍历。
而这道题是不同的,因为题目给出的遍历结果包含了空树,所以仅仅利用一个遍历方式我们就可以构建出二叉树。

只要构建出这棵二叉树,我们再使用中序遍历递归打印节点值就可以了。
难点在,如何使用代码根据前序遍历来构建二叉树?
思路如下:
注意:这里因为题目条件限制,只能使用静态成员i来遍历字符串。但是对于我们实际的应用,不建议使用静态成员,因为当创建多个对象时,他们都会使用并改变i的值,而创建二叉树时,必须从0下标处遍历字符串!!!
7.2 代码- import java.util.Scanner;
-
- //节点
- class TreeNode {
- TreeNode left;
- TreeNode right;
- char val;
-
- public TreeNode(char val) {
- this.val = val;
- }
- }
- // 注意类名必须为 Main, 不要有任何 package xxx 信息
- public class Main {
- public static void main(String[] args) {
- Scanner in = new Scanner(System.in);
- // 注意 hasNext 和 hasNextLine 的区别
- while (in.hasNextLine()) { // 注意 while 处理多个 case
-
- //防止有多组测试用例,每次使用时i要置0
- Main.i = 0;
- String str = in.nextLine();
- TreeNode root = creatTree(str);
- InOrder(root);
- }
-
- }
- public static int i;
- public static TreeNode creatTree(String str) {
- TreeNode node = null;
- if (str.charAt(i) != '#') {
- //不是空树,就实例化该值的节点
- node = new TreeNode(str.charAt(i));
- i++;
- //在递归回退的过程中,完成节点之间的连接
- node.left = creatTree(str);
- node.right = creatTree(str);
- } else {
- //是空树,i++
- i++;
- }
- return node;
- }
- //中序遍历,打印根节点
- public static void InOrder(TreeNode root) {
- if (root == null) {
- return;
- }
- InOrder(root.left);
- System.out.print(root.val + " ");
- InOrder(root.right);
- }
- }
END