day15 二叉树(层次遍历、翻转二叉树、对称二叉树)

二叉树的层次遍历

图论中的深搜和广搜可以应用在不同的数据结构中,二叉树的递归遍历是深搜,层次遍历是广搜
解法一:借助队列实现

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Deque<TreeNode> que = new ArrayDeque<>();
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if(root != null)    que.offerFirst(root);
        while(!que.isEmpty()){
            int size = que.size();
            List<Integer> tmp = new ArrayList<>();
            while( size -- > 0){
                TreeNode cur = que.pollLast();
                tmp.add(cur.val);
                if(cur.left!=null)  que.offerFirst(cur.left);
                if(cur.right!=null) que.offerFirst(cur.right);
            }
            res.add(tmp);
        } 
        return res;
    }
}

解法二:利用二叉树的前序遍历,DFS的思路。依赖前序遍历自顶向下、自左向右的顺序,是从左到右的「列序遍历」。
在这里插入图片描述

class Solution {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        traverse(root,0);
        return res;
    }
    public void traverse(TreeNode root, int depth){
        if(root == null)    return ;
        // 前序位置,看看是否已经存储 depth 层的节点了
        if(res.size() <= depth){
        	// 第一次进入 depth 层
            res.add(new ArrayList<>());
        }
        // 前序位置,在 depth 层添加 root 节点的值
        res.get(depth).add(root.val);
        traverse(root.left,depth+1);
        traverse(root.right,depth+1);
    }
}

解法三:递归解法,自顶向下的「层序遍历」。把二叉树的每一层抽象理解成单链表的一个节点进行遍历

class Solution {
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root == null)    return res;
        List<TreeNode> curLevelNodes = new ArrayList<>();
        curLevelNodes.add(root);
        traverse(curLevelNodes);
        return res;
    }

    public void traverse(List<TreeNode> curLevelNodes){
        if(curLevelNodes.isEmpty())    return ;
        // 前序位置,计算当前层的值和下一层的节点列表
        List<TreeNode> nextLevelNodes = new ArrayList<>();
        List<Integer> tmp = new ArrayList<>();
        for(TreeNode cur : curLevelNodes){
            tmp.add(cur.val);
            if(cur.left!=null)  nextLevelNodes.add(cur.left);
            if(cur.right!=null) nextLevelNodes.add(cur.right);
        }
        // 前序位置添加结果,可以得到自顶向下的层序遍历
        res.add(tmp);
        traverse(nextLevelNodes);
        // 后序位置添加结果,可以得到自底向上的层序遍历结果
    }
}

107.二叉树的层次遍历II
199.二叉树的右视图
637.二叉树的层平均值
429.N叉树的层序遍历
515.在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
104.二叉树的最大深度
111.二叉树的最小深度

226.翻转二叉树

在这里插入图片描述

  • 只需要交换每个节点的左右孩子
  • 遍历顺序为:前序或后序。中序遍历会把某些节点的左右孩子翻转了两次(可以避免,容易踩坑)

解法一:递归解法,遍历的思想

class Solution {
    /**
     * 前后序遍历都可以
     * 中序不行,因为先左孩子交换孩子,再根交换孩子(做完后,右孩子已经变成了原来的左孩子),再右孩子交换孩子(此时其实是对原来的左孩子做交换)
     */
    public TreeNode invertTree(TreeNode root) {
        if(root == null)    return null;
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }
}

解法二:迭代解法,前后序遍历

//前序遍历
class Solution {
    public TreeNode invertTree(TreeNode root) {
        Deque<TreeNode> st = new ArrayDeque<>();
        if(root != null)    st.offerFirst(root);
        while(!st.isEmpty()){
            TreeNode cur = st.pollFirst();
            TreeNode tmp =cur.left;
            cur.left = cur.right;
            cur.right = tmp;
            if(cur.right!=null) st.offerFirst(cur.right);
            if(cur.left!=null)  st.offerFirst(cur.left);
        }
        return root;
    }
}
//后序遍历
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null)    return root;
        Deque<TreeNode> st = new ArrayDeque<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while(cur != null || !st.isEmpty()){
            if(cur != null){
                //遍历到最左节点
                st.offerFirst(cur);
                cur = cur.left;
            }else{
                cur = st.pollFirst();
                //cur.right == null :当前节点为子树根节点
                //cur.right == prev : 右子树是上一次处理的节点则处理根节点
                if(cur.right == null || cur.right == prev){
                    
                    TreeNode tmp =cur.left;
                    cur.left = cur.right;
                    cur.right = tmp;
                    prev = cur;
                    //这一步很重要,置空保证左子树不会被二次遍历。因为处理当前节点后,要向上走了
                    cur = null;
                }else{
                    st.offerFirst(cur);
                    cur = cur.right;
                }
            }
        }
        return root;
    }
}
//统一迭代,前序遍历
class Solution {
    public TreeNode invertTree(TreeNode root) {
        
        Stack<TreeNode> st = new Stack<>();
        if(root != null)     st.push(root);
        while(!st.isEmpty()){
            TreeNode cur = st.pop();
            if(cur != null){
                st.push(cur);
                st.push(null);
                if(cur.right!=null) st.push(cur.right);
                if(cur.left!=null) st.push(cur.left);
            }else{
                cur = st.pop();
                TreeNode tmp = cur.left;
                cur.left = cur.right;
                cur.right = tmp;
            }
        }
        return root;
    }
}

解法三:层次遍历

class Solution {

    public TreeNode invertTree(TreeNode root) {
        Deque<TreeNode> que = new ArrayDeque<>();
        if(root != null)    que.offerFirst(root);
        while(!que.isEmpty()){
            TreeNode cur = que.pollLast();
            TreeNode tmp = cur.left;
            cur.left = cur.right;
            cur.right = tmp;
            if(cur.left!=null) que.offerFirst(cur.left);            
            if(cur.right!=null) que.offerFirst(cur.right);
        }
        return root;
    }
}

101. 对称二叉树

  • 判断根节点的左子树和右子树是否可以沿着中轴线相互翻转
  • 对称二叉树要比较的不是左右孩子,而是两个子树的外侧、内侧是否相等
    在这里插入图片描述

递归解法

  • 遍历顺序:后序,因为上一个节点需要用到子树的信息
  • 一个树的遍历顺序是左右中,一个树的遍历顺序是右左中
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root== null) return true;
        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 outside = compare(left.left,right.right);
        boolean inside = compare(left.right,right.left);
        return outside && inside;
    }
}

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root== null) return true;
        return compare(root.left,root.right);
    }

    public boolean compare(TreeNode l, TreeNode r){
        if(l == null && r == null)  return true;
        else if( l == null || r == null || l.val != r.val)  return false;
        return compare(l.left,r.right) && compare(l.right,r.left);
    }

}

迭代解法

  • 把左右两个子树要比较的元素顺序放进一个容器,然后成对成对的取出来进行比较
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root== null) return true;
        Deque<TreeNode> que = new LinkedList<>();
        que.offerFirst(root.left);
        que.offerFirst(root.right);
        while(!que.isEmpty()){
            TreeNode leftNode = que.pollLast();
            TreeNode rightNode = que.pollLast();
            if(leftNode == null && rightNode == null)  continue;
            if(leftNode == null || rightNode == null || leftNode.val != rightNode.val)  return false;
            que.offerFirst(leftNode.left);
            que.offerFirst(rightNode.right);
            que.offerFirst(leftNode.right);
            que.offerFirst(rightNode.left);
        }
        return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值