代码随想录算法训练营day15|二叉树的层序遍历、翻转二叉树、对称二叉树

2023.3.29 二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

前面的前、中、后序遍历都是通过深度优先遍历来实现的,而我们二叉树的层序遍历需要通过广度优先遍历来实现。

深度优先遍历时通过栈来实现的递归操作,而广度优先遍历需要我们一次遍历一个层的节点,这符合先进先出的理念,需要我们使用队列来完成。

如何才能实现一次遍历一个层的节点呢?对于头结点来说,它一定只有一个节点,我们直接将其存入队列中,记录下其队列长度作为循环次数,从队列中按照之前获取到的队列长度取出节点并记录下其值。结束循环后,我们要判断每个取出的节点是否有左右子节点,如果有,就需要将其加入队列,作为下一层的节点来处理。当上次一循环结束以后,此时队列里面就是下一层的节点数,再次记录下长度,循环取出节点,判断是否有子节点。重复这一过程,直到队列为空。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        //建立一个List
        List<List<Integer>> res = new ArrayList<>();
        //先判断root是否为null
        if(root == null){
            return res;
        }
        //建立一个队列用来存放节点
        //LinkedList实现了队列接口。
        Queue<TreeNode> queue = new LinkedList<>();
        //将根节点放入
        queue.offer(root);
        //退出循环条件,队列为空,即遍历到最后一层。
        while(!queue.isEmpty()){
            //新建一个List用于存放节点数值。
            List<Integer> list = new ArrayList<>();
            //获取队列的长度
            int queueLength = queue.size();
            //根据队列的长度开始循环
            for(int i = 0;i<queueLength;i++){
                //取出队列中的节点
                TreeNode node = queue.poll();
                //将节点值储存进List中
                list.add(node.val);
                if(node.left != null){
                    queue.offer(node.left);
                }
                if(node.right != null){
                    queue.offer(node.right);
                }
            }
            //循环结束,代表一层遍历结束,将子List放回总的结果
            res.add(list);
        }
        //退出while循环代表整个树遍历完成。
        return res;
    }
}

2023.3.29 翻转二叉树

226. 翻转二叉树 - 力扣(LeetCode)

翻转二叉树,实际上我们需要做的就是翻转每个节点的左右子节点。

因此,我们需要做的就是遍历这个二叉树的左右子节点,在遍历时将其翻转。

而我们遍历二叉树学到的有前、中、后序遍历,以及层序遍历。在这里面,前、后序遍历以及层序遍历都可以实现我们的翻转二叉树。

方法一 前序遍历递归实现翻转

以前序遍历为例,我们使用递归方法。判断停止递归的条件仍然是当前节点为null。

先直接交换当前节点的左右子节点,随后递归进入左子节点,在递归进入右子节点,重复这个过程

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null){
            return null;
        }
        swap(root);
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
    public void swap(TreeNode root){
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
    }
}

将翻转的过程设置为一个方法更符合面向对象的思想。

方法二 前序遍历迭代法实现翻转

既然我们可以用迭代法实现前序遍历,那么通过前序遍历的迭代法实现翻转也是可以的

总体思路不变,只不过将记录值的操作变为了翻转左右两个节点的操作。

class Solution {
    public TreeNode invertTree(TreeNode root) {
        //借助栈来储存节点
        Deque<TreeNode> stack = new ArrayDeque<>();
        //使用一个辅助变量来记录当前遍历的节点
        TreeNode temp = root;
        //当我们的栈不为空或者temp不为null时,就可以继续遍历。
        //temp不为null是为了进入第一次循环,stack不为null是为了后续的循环。
        while(!stack.isEmpty() || temp != null){
            while(temp != null){//只要当前节点不为null,就可以继续遍历
                //当前节点不为null,直接交换当前节点的左右两个子节点
                TreeNode newTemp = temp.left;
                temp.left = temp.right;
                temp.right = newTemp;
                //将当前节点入栈
                stack.push(temp);
                //将当前节点变为左节点
                temp = temp.left;
            }
            //退出循环,代表当前节点为null值,需要返回栈中的节点去遍历其右子节点
            temp = stack.pop();
            temp = temp.right;
        }
        return root;
    }
}

方法三 层序遍历实现翻转二叉树

前面两种方法都是使用深度优先来遍历的二叉树,我们也可以使用广度优先遍历去遍历二叉树来实现节点的翻转。

class Solution {
    public TreeNode invertTree(TreeNode root) {
        //使用队列来储存节点
        Queue<TreeNode> queue = new LinkedList<>();
        if(root == null) return null;
        //将节点计入队列中
        queue.offer(root);
        while(!queue.isEmpty()){
            int length = queue.size();
            for(int i = 0;i<length;i++){
                TreeNode temp = queue.poll();
                //完成子节点的交换工作。
                TreeNode newTemp = temp.left;
                temp.left = temp.right;
                temp.right = newTemp;
                if(temp.left != null ) queue.offer(temp.left);
                if(temp.right != null) queue.offer(temp.right);
            }
        }
        return root;
    }
}

2023.3.29 对称二叉树

101. 对称二叉树 - 力扣(LeetCode)

看一个二叉树是否对称,我们需要看它的内外侧是否相等,根据递归的三部曲,我们先确定返回值,显然是boolean,因为要进行正确与否的判断,传入的参数,由于需要判断内外两侧,因此会存在两个参数,一个左节点,一个右节点。

终止条件,节点为空时,有两种情况,一个为空,一个不为空,返回false,两个为空,返回true。当节点不为空时,判断值是否相等,不相等,返回false,如果相等,进入下一层递归中,直到完全相等时,会因为有两个空的子节点而发返回true。

方法一 深度优先的递归法

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 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;
        }
        //四种条件判断都没有进入,代表两个节点不为null,且val值相等,继续判断其子节点的内外层的堆成情况
        boolean outside = compare(left.left,right.right);
        boolean inside = compare(left.right,right.left);
        //只有外侧对称且内侧对称才返回true;
        return outside && inside;
    }
}

方法二 广度优先的迭代法对比

从根节点的左右两个子节点开始,我们要判断其是否对称,在这个层上,外侧节点和内侧节点一定要满足对称的要求。因此,我们可以使用队列,将每个匹配的内外侧节点依次加入队列里面,然后判断节点间的信息。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        //借助队列来实现
        Queue<TreeNode> queue = new LinkedList<>();
        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;
            }
            //如果从队列中取出的节点有一个为空而另一个不为null,或者值不相等,直接返回false
            if(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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值