代码随想录DAY14|226翻转二叉树、101对称二叉树、104二叉树最大深度、111二叉树最小深度

1. 翻转二叉树

力扣

每次对二叉树进行处理时,先想清楚该题要用到哪种遍历方式。就比如本题可以用前后序遍历和逐层遍历。前序遍历也可以用但是要麻烦一些。

遍历每个节点,将其左右节点翻转即可。
可以用前后序遍历和层序遍历。但是不能用中序遍历,因为会将有的节点翻转两次。

  • 递归方法

递归三部曲

  1. 确定函数的参数和返回值
    每次要传入的是一个节点,而且并不需要返回值。
  2. 终止条件
    当前节点为空就return。
  3. 确定递归逻辑
    以前序为例,就先处理本节点,翻转左右子节点,然后递归下去。
class Solution {
   /**
     * 前后序遍历都可以
     * 中序不行,因为先左孩子交换孩子,再根交换孩子(做完后,右孩子已经变成了原来的左孩子),再右孩子交换孩子(此时其实是对原来的左孩子做交换)
     */
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        invertTree(root.left);
        invertTree(root.right);
        swapChildren(root);
        return root;
    }

    private void swapChildren(TreeNode root) {
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
    }
}
  • 迭代方式

用层序遍历的话,处理每一个节点就可以了。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode() {}
 * TreeNode(int val) { this.val = val; }
 * TreeNode(int val, TreeNode left, TreeNode right) {
 * this.val = val;
 * this.left = left;
 * this.right = right;
 * }
 * }
 */
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return root;
        }
        Deque<TreeNode> deque = new LinkedList<>();
        deque.add(root);
        while (!deque.isEmpty()) {
            int num = deque.size();
            while (num > 0) {
                TreeNode node = deque.poll();
                TreeNode temp = node.left;
                node.left = node.right;
                node.right = temp;
                if (node.left != null) {
                    deque.add(node.left);
                }
                if (node.right != null) {
                    deque.add(node.right);
                }
                num--;
            }
        }
        return root;
    }
}
  • 补充

也可以用中序遍历,不过要将处理右节点改为处理左节点,因为处理中的时候将二者翻转了。

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        invertTree(root->left);         // 左
        swap(root->left, root->right);  // 中
        invertTree(root->left);         // 注意 这里依然要遍历左孩子,因为中间节点已经翻转了
        return root;
    }
};

2. 对称二叉树

力扣

递归:每次处理的是根节点的两个子树,看这两个子树能不能对称。每次处理时先排除掉一些简单的情况。然后比较对称位置节点的左右子树。
遍历顺序只能是后序遍历,因为要先收集两个子树的对称情况,返回给父节点做判断。
迭代:使用层序遍历,用队列或栈记录每一层的结果,比较结果是否对称。

  • 递归

递归三部曲

  1. 函数参数和返回值
    每次比较的是两个子树,要传入两个节点。要返回boolean值。
  2. 终止条件
    要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚。否则后面比较数值的时候就会操作空指针了。一共有四种情况:左空,右空,左右空,左右值不等。换句话说,终止条件是可以得出一个结果的时候
		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;
        }
  1. 每层处理逻辑
    先看子树的内外侧是否对称,然后返回给本节点。
class Solution {
    public boolean isSymmetric(TreeNode root) {
        return compare(root.left, root.right);
    }
    public boolean compare(TreeNode left, TreeNode right){
        if(left != null && right == null){
            return false;
        }else if(left == null && right != null){
            return false;
        }else if(left == null && right == null){
            return true;
        }else 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 isSymmetric3(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) {
//                return false;
//            }
//            if (leftNode != null && rightNode == null) {
//                return false;
//            }
//            if (leftNode.val != rightNode.val) {
//                return false;
//            }
            // 以上三个判断条件合并
            if (leftNode == null || rightNode == null || leftNode.val != rightNode.val) {
                return false;
            }
            // 这里顺序与使用Deque不同
            deque.offer(leftNode.left);
            deque.offer(rightNode.right);
            deque.offer(leftNode.right);
            deque.offer(rightNode.left);
        }
        return true;
    }

3. 最大深度

力扣

本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
    根节点的高度就是二叉树的最大深度。
  • 递归
  1. 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
  2. 确定终止条件:如果为空节点的话,就返回0,表示高度为0。
  3. 确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        return compareHeight(root);
    }
    public int compareHeight(TreeNode node){
        if(node == null){
            return 0;
        }
        int leftHeight = compareHeight(node.left);
        int rightHeight = compareHeight(node.right);
        return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }
}

  • 迭代

层序遍历,每一层将depth+1即可。

class Solution {
    /**
     * 迭代法,使用层序遍历
     */
    public int maxDepth(TreeNode root) {
        if(root == null) {
            return 0;
        }
        Deque<TreeNode> deque = new LinkedList<>();
        deque.offer(root);
        int depth = 0;
        while (!deque.isEmpty()) {
            int size = deque.size();
            depth++;
            for (int i = 0; i < size; i++) {
                TreeNode node = deque.poll();
                if (node.left != null) {
                    deque.offer(node.left);
                }
                if (node.right != null) {
                    deque.offer(node.right);
                }
            }
        }
        return depth;
    }
}

4. 二叉树最小深度

力扣
和最大深度差不多,但是要注意一点:左子节点为空时不算子树高度为0。
如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。

  • 迭代
    迭代的时候,遇到叶子节点返回depth即可。
class Solution {
    public int minDepth(TreeNode root) {
        if(root == null){return 0;}
        Deque<TreeNode> deque = new LinkedList<>();
        int depth = 0;
        deque.push(root);
        while(!deque.isEmpty()){
            int size = deque.size();
            depth++;
            while(size > 0){
                TreeNode node = deque.pop();
                if (node.left != null) {
                    deque.offer(node.left);
                }
                if (node.right != null) {
                    deque.offer(node.right);
                }
                if(node.left == null && node.right == null){
                    return depth;
                }
                size --;
            }
        }
        return depth;
    }
}
  • 递归
class Solution {
    /**
     * 递归法,相比求MaxDepth要复杂点
     * 因为最小深度是从根节点到最近**叶子节点**的最短路径上的节点数量
     */
    public int minDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftDepth = minDepth(root.left);
        int rightDepth = minDepth(root.right);
        if (root.left == null) {
            return rightDepth + 1;
        }
        if (root.right == null) {
            return leftDepth + 1;
        }
        // 左右结点都不为null
        return Math.min(leftDepth, rightDepth) + 1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值