目录
一、检查两棵树是否相同
题目:相同的树 100.相同的树
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例:
思路:
两棵树相同的条件:根结点一样,左右子树相同(递归问题)。
考虑有空树的情况:
- p == null && q != null : 不是相同的树
- p != null && q == null : 不是相同的树
- p == null && q == null : 是相同的树(都是空树)
代码:
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
//两棵树都为空树的情况
if (p == null && q == null) {
return true;
}
//其中有一棵空树的情况
//用 || 是因为如果是两棵树都为空树,在上一个 if 中已经判断出来了,不会走到这里
if (p == null || q == null) {
return false;
}
//代码执行到这里说明两棵树都不为空
return (p.val == q.val) && (isSameTree(p.left,q.left)) && (isSameTree(p.right,q.right));
//分开写:(等价于上面一句)
// if (p.val != q.val) {
// return false;
// }
// if (!isSameTree(p.left,q.left)) {
// return false;
// }
// if (!isSameTree(p.right,q.right)) {
// return false;
// } else {
// return true;
// }
}
}
二、是否是另一棵树的子树
题目:另一棵树的子树 572.另一棵树的子树
给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。
二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
示例:
思路:
要判断一棵树是否是另一棵树的子树,则需要分成两个子问题:查找结点和判断是否是相同的树:
1.查找结点,若树中没有目标子树的根结点,则返回 false,如果有则继续判断。
2.判断是否是相同的树:根结点一样,左右子树也相同(递归)。
注意:因为可能会有多个和目标值相同的结点,所以要把所有和目标值相同的结点都找出来,依次判断。在这些结点中:存在相同左右子树的:返回true;都不相同:返回 false;
因为需要找出所有和目标值相同的元素,所以使用链表将其全部存储起来。
代码:
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
List<TreeNode> ans = new ArrayList<>();
contains(root,subRoot,ans);
//得到所有跟目标值相同的结点,依次判断
for (TreeNode r : ans) {
if (isSameTree(r,subRoot)) {
return true;
}
}
//循环结束,没有相同的
return false;
}
//查找结点
public void contains(TreeNode p, TreeNode q, List<TreeNode> ans) {
if (p == null) {
return ;
}
//查看根结点是否和目标结点相同
if (p.val == q.val) {
ans.add(p);
}
//继续查找其左右子树中是否有目标结点
contains(p.left,q,ans);
contains(p.right,q,ans);
}
//判断是否是相同的树
public boolean isSameTree(TreeNode p,TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
//说明两个树都不为空树
return (p.val == q.val) && isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
}
三、翻转二叉树
题目:翻转二叉树 226.翻转二叉树
给你一棵二叉树的根节点 root
,翻转这棵二叉树,并返回其根节点。
示例:
思路: 翻转二叉树:根结点不变,左子树变成右子树,右子树变成左子树
思路1:新定义一个结点,将根结点复制过去,再将原树的左子树变成新结点的右子树,将原树的右子树变成新结点的左子树。注意:当树为空树时,返回 null。
代码:
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode ans = new TreeNode(root.val);
ans.left = invertTree(root.right);
ans.right = invertTree(root.left);
return ans;
}
}
思路2:在原来的树上直接进行操作(前序遍历,先交换元素,再遍历左右子树)。根结点不变,变换左右子树(注意到底是左子树还是右子树)。
代码:
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
//翻转左右子树
TreeNode tmp = root.right;
root.right = root.left;
root.left = tmp;
//翻转左右子树
invertTree(root.left);
invertTree(root.right);
return root;
}
}
四、判断一棵树是否是平衡二叉树
题目:平衡二叉树 110.平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例:
思路:
遍历思路:遍历每一个结点,获取两个子树的高度,比较高度差。一旦存在 |高度差| > 1,说明不是平衡二叉树。若全部 |高度差| <= 1,则说明是平衡二叉树。
子问题思路:
- 根:|左子树高度 - 右子树高度| <= 1
- 左子树:是一棵平衡二叉树
- 右子树:是一棵平衡二叉树
代码:这里使用子问题思路
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
//子问题思路
if ((leftHeight - rightHeight) > 1 || (rightHeight - leftHeight) > 1) {
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);
return Integer.max(leftHeight,rightHeight) + 1;
}
}
五、判断两棵树是否互为镜像
思路:
假设两棵树都不是空树,则互为镜像的条件:(规模较小的子树相同问题(递归))
- p 的根的值 == q 的根的值
- p 的左子树 和 q 的左子树互为镜像;
- p 的右子树 和 q 的右子树互为镜像。
涉及到空树的情况:
- p == null && q == null -> true
- p == null && q != null -> false
- p != null && q == null -> false
代码:
public boolean isMirror(TreeNode p, TreeNode q) {
//先处理涉及到空树的情况
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
//代码执行到这里说明两棵树均不为空,子问题思路
//注意互为镜像,p 的左子树应该对应 q 的右子树,p 的右子树对应 q 的左子树
return p.val == q.val
&& isMirror(p.left,q.right)
&& isMirror(p.right,q.left);
}
六、判断一棵树是否是对称二叉树
题目:对称二叉树 101.对称二叉树
给你一个二叉树的根节点 root
, 检查它是否轴对称。
示例:
思路:利用是否互为镜像,即左右子树是否互为镜像。
将判断是否对称(复杂问题)-> 判断是否互为镜像(简单问题)。即将复杂问题化成小问题(子问题)
小问题:
- 可能是另一种问题—— 子步骤
- 也可能是规模较小的相同问题—— 递归
代码:
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
//不为空树
return isMirror(root.left,root.right);
}
//是否互为镜像
public boolean isMirror(TreeNode p,TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
return p.val == q.val
&& isMirror(p.left,q.right)
&& isMirror(p.right,q.left);
}
}