文章目录
- LeetCode 226.翻转二叉树
- 题目链接🔗
- 思路
- 递归法
- 迭代法
- LeetCode 101. 对称二叉树
- 题目链接🔗
- 思路
- 递归法
- 迭代法
- 相关题目
LeetCode 226.翻转二叉树
题目链接🔗
LeetCode 226.翻转二叉树
思路
这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!建议拿纸画一画,就理解了
那么层序遍历可以不可以呢?依然可以的!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!
递归法
我们来看一下递归三部曲:
-
确定递归函数的参数和返回值
参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode。
TreeNode invertTree(TreeNode root)
-
确定终止条件
当前节点为空的时候,就返回if(root == null){ return root; }
-
确定单层递归的逻辑
因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。swapChildren(root); invertTree(root.left); invertTree(root.right);
最终完整的代码为:
class Solution {
/**
* 前后序遍历都可以
* 中序不行,因为先左孩子交换孩子,再根交换孩子(做完后,右孩子已经变成了原来的左孩子),
* 再右孩子交换孩子(此时其实是对原来的左孩子做交换)
*/
public TreeNode invertTree(TreeNode root) {
if(root == null){
return root;
}
swapChildren(root); //中
invertTree(root.left); //左
invertTree(root.right); //右
return root;
}
private void swapChildren(TreeNode root) {
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
}
}
迭代法
如果对迭代法不清除的,可以去看二叉树的遍历
//BFS 广度优先遍历 也就是层序遍历
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {return null;}
ArrayDeque<TreeNode> deque = new ArrayDeque<>();
deque.offer(root);
while (!deque.isEmpty()) {
int size = deque.size();
while (size-- > 0) {
TreeNode node = deque.poll();
swap(node);
if (node.left != null) deque.offer(node.left);
if (node.right != null) deque.offer(node.right);
}
}
return root;
}
public void swap(TreeNode root) {
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
}
}
LeetCode 101. 对称二叉树
题目链接🔗
LeetCode 101. 对称二叉树
思路
对于二叉树是否对称,要比较的是根节点的左子树与右子树是不是相互翻转的,理解这一点就知道了其实我们要比较的是两个树(这两个树是根节点的左右子树),所以在递归遍历的过程中,也是要同时遍历两棵树。
本题遍历只能是“后序遍历”,因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。
正是因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
但都可以理解算是后序遍历,尽管已经不是严格上在一个树上进行遍历的后序遍历了。
递归法
递归三部曲
- 确定函数的参数和返回值
我们是比较的是根节点的两个子树是否相互翻转,进而判断这个树是不是对称树,所以要比较两个树,参数自然是左子树结点和右子树结点
返回值自然是布尔类型
boolean compare(TreeNode left, TreeNode right);
- 确定终止条件
要比较两个结点数值相不相同,首先要把两个结点为空的情况弄清除。
结点为空的情况有:
- 左节点为空,右节点不为空,返回false
- 右节点为空,左节点不为空,返回false
- 两个结点都为空,返回true
此时已经排除了结点为空的情况,那么剩下的就是左右结点不为空
左右都不为空,比较节点数值,不相同就return false
此时左右节点不为空,且数值也不相同的情况我们也处理了。
if(left == null && right != null) return false;
else if(left != null && right == null) return false;
else if(left == null && right == nul) return true;
else if(left.val != right.val) return false;
注意上面最后一种情况,没有使用else,而是else if, 因为我们把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况。
- 确定单层递归逻辑
单层递归的逻辑就是处理左右结点都不为空且数值相同的情况
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
boolean outside = compare(left.left, right.right); // 左子树:左、 右子树:右
boolean inside = compare(left.right, right.left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理)
return isSame;
如上代码中,我们可以看出使用的遍历方式,左子树左右中,右子树右左中,所以我把这个遍历顺序也称之为“后序遍历”(尽管不是严格的后序遍历)
完整代码:
public boolean isSymmetric1(TreeNode root) {
return compare(root.left, root.right);
}
private boolean compare(TreeNode left, TreeNode right) {
if (left == null && right != null) {
return false;
}
if (left != null && right == null) {
return false;
}
if (left == null && right == null) {
return true;
}
if (left.val != right.val) {
return false;
}
// 比较外侧
boolean compareOutside = compare(left.left, right.right);
// 比较内侧
boolean compareInside = compare(left.right, right.left);
return compareOutside && compareInside;
}
迭代法
这道题目我们也可以使用迭代法,但要注意,这里的迭代法可不是前中后序的迭代写法,因为本题的本质是判断两个树是否是相互翻转的,其实已经不是所谓二叉树遍历的前中后序的关系了。
这里我们可以使用队列来比较两个树(根节点的左右子树)是否相互翻转
使用普通队列
public boolean isSymmetric(TreeNode root) {
Queue<TreeNode> deque = new LinkedList<>();
deque.offer(root.left);
deque.offer(root.right);
while(!deque.isEmpty()){
TreeNode leftNode = deque.poll();
TreeNode rightNode = deque.poll();
if(leftNode == null && rightNode == null){
continue;
}
if(leftNode == null || rightNode == null || leftNode.val != rightNode.val){
return false;
}
deque.offer(leftNode.left);
deque.offer(rightNode.right);
deque.offer(leftNode.right);
deque.offer(rightNode.left);
}
return true;
}
使用双端队列,相当于两个栈
public boolean isSymmetric2(TreeNode root) {
Deque<TreeNode> deque = new LinkedList<>();
deque.offerFirst(root.left);
deque.offerLast(root.right);
while (!deque.isEmpty()) {
TreeNode leftNode = deque.pollFirst();
TreeNode rightNode = deque.pollLast();
if (leftNode == null && rightNode == null) {
continue;
}
if (leftNode == null || rightNode == null || leftNode.val != rightNode.val) {
return false;
}
deque.offerFirst(leftNode.left);
deque.offerFirst(leftNode.right);
deque.offerLast(rightNode.right);
deque.offerLast(rightNode.left);
}
return true;
}
相关题目
100. 相同的树
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
return compare(p, q);
}
private boolean compare(TreeNode left, TreeNode right){
if(left == null && right == null){
return true;
}
if(left == null || right == null || left.val != right.val){
return false;
}
boolean leftside = compare(left.left, right.left);
boolean rightside = compare(left.right, right.right);
return leftside && rightside;
}
}
572. 另一棵树的子树
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null){
return false;
}
if(root.val == subRoot.val && compare(root, subRoot)){
return true;
}
return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
}
boolean compare(TreeNode root, TreeNode node){
if(root == null && node == null){
return true;
}
if(root == null || node == null || root.val != node.val){
return false;
}
return compare(root.left, node.left) && compare(root.right, node.right);
}
}