对称二叉树
题目描述
力扣链接🔗
递归法分析
分成内外侧进行比较
递归的三大步:
- 确定递归函数的参数和返回值
因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。返回值自然是bool类型。 - 确定终止条件
要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。
节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空: - 左右都不为空,比较节点数值,不相同就return false
此时左右节点不为空,且数值也不相同的情况我们也处理了。
- 确定单层递归的逻辑
此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
总的来说:
就是判断两个节点的四种情况:
- 都为空,对称,返回true
- 一空一不空,不对称
- 两个取值不相等,不对称
- 取值相等,但是孩子节点不一定相等,所以需要递归继续判断左右孩子,才能判断是否相等
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
return compare(root.left, root.right);
}
/**
* 判断左右节点是否对称
*
* @param left
* @param right
* @return true表示对称,false表示不对称
*/
boolean compare(TreeNode left, TreeNode right) {
if (left == null && right == null) return true; // 左右节点都为空,则是对称的
else if (left == null && right != null) return false; // 左节点为空,右节点不为空,此时不对称
else if (left != null && right == null) return false; // 左节点不为空,右节点为空,此时不对称
else if (left.val != right.val) return false; // 左右节点的值不相等,此时不对称
else { // 剩下的是左右节点相等的情况,此时递归判断孩子节点
boolean outside = compare(left.left, right.right); // 判断外侧
boolean inside = compare(left.right, right.left); // 判断内侧
return inside && outside; // 如果该节点的左右孩子节点都是对称的,返回true
}
}
}
使用队列和栈
使用队列的演示图:
通过队列来判断根节点的左子树和右子树的内侧和外侧是否相等。
/**
* 使用队列实现
*
* @param root
* @return
*/
public boolean isSymmetric(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>(); // 辅助队列进行比较
if (root == null) {
return true;
}
queue.offer(root.left);
queue.offer(root.right);
while (!queue.isEmpty()) {
// 成对的取出元素进行判断
TreeNode leftNode = queue.poll();
TreeNode rightNode = queue.poll();
if (leftNode == null && rightNode == null) continue; // 左右节点为空,为对称
if (leftNode == null && rightNode != null
|| leftNode != null && rightNode == null
|| leftNode.val != rightNode.val)
return false; // 三种情况为不对成
queue.offer(leftNode.left); // 加入左节点左孩子
queue.offer(rightNode.right); // 加入右节点右孩子
queue.offer(leftNode.right); // 加入左节点右孩子
queue.offer(rightNode.left); // 加入右节点左孩子
}
return true;
}
}
将队列换成栈也可以实现,本质不变,都是成对的取出节点进行比较,再将孩子入栈或队列。
类似题目
相同的树
题目描述
力扣链接🔗
代码分析
比较两棵树,同时比较左侧和右侧。
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) return true;
else if (p == null && q != null || p != null && q == null) return false;
else if (p.val != q.val) return false;
else {
// 注意此时不是比较内外侧,而是两棵树
boolean outside = isSameTree(p.left, q.left); // 比较两棵树的左孩子
boolean inside = isSameTree(p.right, q.right); // 比较两棵树的右孩子
return outside && inside;
}
}
}
另一棵子树
题目描述
力扣链接🔗
代码分析
深度遍历每个节点,再将遍历的节点进行判断和subRoot是否相同即可。
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if (root == null && subRoot == null) return true;
return dfs(root, subRoot);
}
/**
* 深度遍历判断
*
* @param root
* @param subRoot
* @return
*/
public boolean dfs(TreeNode root, TreeNode subRoot) {
if (root == null) {
return false;
}
boolean curCompare = compare(root, subRoot); // 先比较当前节点
boolean leftCompare = dfs(root.left, subRoot); // 遍历左孩子
boolean rightCompare = dfs(root.right, subRoot); // 遍历右孩子
return curCompare || rightCompare || leftCompare;
}
public boolean compare(TreeNode rootNode, TreeNode subRoot) {
if (rootNode == null && subRoot == null) {
return true;
}
if (rootNode == null && subRoot != null || subRoot == null && rootNode != null) {
return false;
}
if (rootNode.val != subRoot.val) {
return false;
}
return compare(rootNode.left, subRoot.left) && compare(rootNode.right, subRoot.right);
}
}