【二叉树】|代码随想录算法训练营第11天|二叉树递归遍历、二叉树迭代遍历、二叉树层序遍历

刷题神器

代码随想录

往期回顾

>【栈与队列】|代码随想录算法训练营第10天|150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素、【总结】

题目

二叉树理论基础

文章:文章讲解
视频:视频讲解

二叉树种类
  • 满二叉树

    除最底层叶子节点,其余所有的节点都有左右子节点

    深度为k,有2^k-1个节点的二叉树

  • 完全二叉树

    最后一层子节点,从左到右连续的,如果有不连续的就不是完全二叉树

  • 二叉搜索树

    二叉搜索树是一个有序树,如果左子树不空,左子树所有的值都小于根节点的值,如果右子树不空,右子树所有的节点都大于根节点,左右子树也是二叉排序树

  • 平衡二叉搜索树

    左右子树的高度绝对值不超过1, 并且左右子树也是平衡二叉树

二叉树的存储方式

二叉树可以链式存储,也可以顺序存储。

链式存储是通过链表左右子针的方式存储的

顺序存储,用的数组方式存储的,如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

二叉树的遍历方式
  • 深度优先遍历
    • 前序遍历(递归,迭代法) 中 左 右
    • 中序遍历(递归,迭代法) 左 中 右
    • 后续遍历(递归,迭代法) 左 右 中
  • 广度优先遍历
    • 层次遍历(迭代法)
二叉树的定义
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;
    }
}

二叉树的递归遍历

题目:1.前序遍历

2.中序遍历

3.后序遍历
文章:文章讲解
视频:视频讲解

  • 学后思路

    递归三要素 :1.确定递归函数的参数和返回值 2.确定终止条件 3.确定单层递归的逻辑

    确定循环的顺序

解法:

// 前序遍历 中 左 右
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        preOrder(result, root);
        return result;
    }

    public void preOrder(List<Integer> result, TreeNode root) {
        if (root == null) {
            return;
        }
        result.add(root.val);
        preOrder(result, root.left);
        preOrder(result, root.right);
    }
}

// 后序遍历 左 右 中
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        postOrder(result, root);
        return result;
    }

    public void postOrder(List<Integer> result, TreeNode root) {
        if (root == null) {
            return;
        }
        postOrder(result, root.left);
        postOrder(result, root.right);
        result.add(root.val);
    }
}

// 中序遍历 左 中 右
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        inOrder(result, root);
        return result;
    }

    public void inOrder(List<Integer> result, TreeNode root) {
        if (root == null) {
            return;
        }
        inOrder(result, root.left);
        result.add(root.val);
        inOrder(result, root.right);
    }
}
  • 题目总结
    • 二叉树的遍历,注意前中后的排序,注意判断条件,什么时候跳出

二叉树的迭代遍历

题目:1.前序遍历

2.中序遍历

3.后序遍历
文章:文章讲解
视频:视频讲解-前后

视频讲解-中

  • 学后思路
    迭代遍历主要是使用栈来实现遍历,前序遍历和后序遍历只需要做反转逻辑,中序遍历需要使用指针

解法一:

// 前序遍历 中 左 右 
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        if (root == null)
            return result;
        Stack<TreeNode> stack = new Stack();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            result.add(node.val);	
            if (node.right != null)
                stack.push(node.right);
            if (node.left != null)
                stack.push(node.left);
        }
        return result;
    }
}

// 后续遍历 左右中
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        if (root == null)
            return result;
        Stack<TreeNode> stack = new Stack();
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.left != null)
                stack.push(node.left);
            if (node.right != null)
                stack.push(node.right);
        }
        Collections.reverse(result);
        return result;
    }
}

// 中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        Stack<TreeNode> stack = new Stack();
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            // 处理左指针
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            } else {
                // 当左指针为null的时候
                cur = stack.pop();
                result.add(cur.val);
                // 判断右节点
                cur = cur.right;
            }
        }
        return result;
    }
}
  • 题目总结
    • 使用迭代遍历,前序和后序要相对简单,前序最简单,后续需要再前序的基础上进行反转
    • 中序遍历需要配合指针只用,注意判断条件,什么时候弹出,什么时候进栈

二叉树的统一迭代法

题目:题目链接
文章:文章讲解
视频:无

  • 学后思路
    使用填充空节点的方法来统一三种迭代方法

解法一:

暂时不处理

题目总结

  • 主要注意如何补充空节点,这样就是为了补充统一格式

二叉树层序遍历登场

文章:文章讲解
视频:视频讲解

  • 学后思路
    层序遍历,广度优先搜索,借用队列
102.二叉树的层序遍历

题目:题目链接

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        Queue<TreeNode> deque = new LinkedList<>();
        if (root == null)
            return result;
        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            List<Integer> rs = new ArrayList<>();
            while (size > 0) {
                TreeNode pop = deque.poll();
                rs.add(pop.val);
                if (pop.left != null)
                    deque.add(pop.left);
                if (pop.right != null)
                    deque.add(pop.right);
                size--;
            }
            result.add(rs);
        }
        return result;
    }
}

题目总结

  • 注意队列的size判断,注意进队出队
107. 二叉树的层序遍历 II

题目:题目链接

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        Queue<TreeNode> deque = new LinkedList<>();
        if (root == null)
            return result;
        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            List<Integer> rs = new ArrayList<>();
            while (size > 0) {
                TreeNode pop = deque.poll();
                rs.add(pop.val);
                if (pop.left != null)
                    deque.add(pop.left);
                if (pop.right != null)
                    deque.add(pop.right);
                size--;
            }
            result.add(rs);
        }
        Collections.reverse(result);
        return result;
    }
}

题目总结

  • 从下至上遍历,只需要将正序遍历的结果反转即可
199.二叉树的右视图

题目:题目链接

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> rs = new ArrayList<>();
        Queue<TreeNode> deque = new LinkedList<>();
        if (root == null)
            return rs;
        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            while (size > 0) {
                TreeNode pop = deque.poll();
                if (size == 1) {
                    rs.add(pop.val);
                }
                if (pop.left != null)
                    deque.add(pop.left);
                if (pop.right != null)
                    deque.add(pop.right);
                size--;
            }
        }
        return rs;
    }
}

题目总结

  • 从右侧看,只保存最右侧节点, 所以要判断每一层的最后一个节点加入到结果集
637. 二叉树的层平均值

题目:题目链接

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> result = new ArrayList<>();
        Queue<TreeNode> deque = new LinkedList<>();
        if (root == null)
            return result;
        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            List<Integer> rs = new ArrayList<>();
            while (size > 0) {
                TreeNode pop = deque.poll();
                rs.add(pop.val);
                if (pop.left != null)
                    deque.add(pop.left);
                if (pop.right != null)
                    deque.add(pop.right);
                size--;
            }
            // 注意这个位置的类型转换
            Long rowTotal = rs.stream().mapToLong(r -> Long.valueOf(r)).reduce(Long::sum).getAsLong();
            Double ave = rowTotal / Double.valueOf(rs.size());
            result.add(ave);
        }
        return result;
    }
}

题目总结

  • 这道题的重点是关注除法,每一行的和可能超过int的最大值,所以要用long,size要转换成double
429. N 叉树的层序遍历

题目:题目链接

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> result = new ArrayList<>();
        Queue<Node> deque = new LinkedList<>();
        if (root == null)
            return result;
        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            List<Integer> rs = new ArrayList<>();
            while (size > 0) {
                Node pop = deque.poll();
                rs.add(pop.val);
                // 注意节点没有左右子树,是多子树
                if (pop.children != null) {
                    for (Node no : pop.children) {
                        deque.add(no);
                    }
                }
                size--;
            }
            result.add(rs);
        }
        return result;
    }
}

题目总结

  • 注意节点,不在分左右子树,有多子节点,所以处理子节点的时候要遍历处理,不是判断左右子树
515. 在每个树行中找最大值

题目:题目链接

class Solution {
    public List<Integer> largestValues(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Queue<TreeNode> deque = new LinkedList<>();
        if (root == null)
            return result;
        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            List<Integer> rs = new ArrayList<>();
            while (size > 0) {
                TreeNode pop = deque.poll();
                rs.add(pop.val);
                if (pop.left != null)
                    deque.add(pop.left);
                if (pop.right != null)
                    deque.add(pop.right);
                size--;
            }
            Integer max = rs.stream().max(Comparator.comparing(Integer::intValue)).get();
            result.add(max);
        }
        return result;
    }
}

题目总结

  • 取每一层的最大值,可以全部存起来再判断最大值,也可以再遍历的过程中判断最大值
116. 填充每个节点的下一个右侧节点指针

题目:题目链接

class Solution {
    public Node connect(Node root) {
        if (root == null)
            return root;
        Queue<Node> deque = new LinkedList<>();

        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            //List<Node> rs = new ArrayList<>();
            while (size > 0) {
                Node pop = deque.poll();
                // rs.add(pop);
                if(size > 1){
                    pop.next = deque.peek();
                }
                if (pop.left != null)
                    deque.add(pop.left);
                if (pop.right != null)
                    deque.add(pop.right);
                size--;
            }
            //if (rs.size() == 1)
            //    continue;
            //int slow = 0;
            //int fast = 1;
            //while (fast < rs.size()) {
            //    rs.get(slow).next = rs.get(fast);
            //    slow++;
            //    fast++;
            //}
        }
        return root;
    }
}

题目总结

  • 这个题目有两种做法,在弹出每一个的时候判断是不是最后一个,如果不是最后一个可以当前队列的第一个付给next,也可以全部存在一个list中,然后用快慢指针赋值next
117. 填充每个节点的下一个右侧节点指针 II

题目:题目链接

class Solution {
    public Node connect(Node root) {
        if (root == null)
            return root;
        Queue<Node> deque = new LinkedList<>();

        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            //List<Node> rs = new ArrayList<>();
            while (size > 0) {
                Node pop = deque.poll();
                // rs.add(pop);
                if(size > 1){
                    pop.next = deque.peek();
                }
                if (pop.left != null)
                    deque.add(pop.left);
                if (pop.right != null)
                    deque.add(pop.right);
                size--;
            }
            //if (rs.size() == 1)
            //    continue;
            //int slow = 0;
            //int fast = 1;
            //while (fast < rs.size()) {
            //    rs.get(slow).next = rs.get(fast);
            //    slow++;
            //    fast++;
            //}
        }
        return root;
    }
}

题目总结

  • 结题思路与116类似,代码一样
104. 二叉树的最大深度

题目:题目链接

class Solution {
    public int maxDepth(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        Queue<TreeNode> deque = new LinkedList<>();
        if (root == null)
            return result.size();
        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            List<Integer> rs = new ArrayList<>();
            while (size > 0) {
                TreeNode pop = deque.poll();
                rs.add(pop.val);
                if (pop.left != null)
                    deque.add(pop.left);
                if (pop.right != null)
                    deque.add(pop.right);
                size--;
            }
            result.add(rs);
        }
        return result.size();
    }
}

题目总结

  • 取最大深度,换个思路就是取有多少层,我们把结果收集起来以后,list的长度就是层数,当然也可以不用记录list,直接在循环结束的时候计数也可以
111. 二叉树的最小深度

题目:题目链接

class Solution {
    public int minDepth(TreeNode root) {
        if (root == null)
            return 0;
        Queue<TreeNode> deque = new LinkedList<>();
        int depth = 1;
        deque.add(root);
        while (!deque.isEmpty()) {
            int size = deque.size();
            while (size > 0) {
                TreeNode pop = deque.poll();
                // 判断最短路径
                if (pop.left == null && pop.right == null) {
                    return depth;
                }
                if (pop.left != null)
                    deque.add(pop.left);
                if (pop.right != null)
                    deque.add(pop.right);
                size--;
            }
            depth++;
        }
        return 0;
    }
}

题目总结

  • 最小深度,可以同等为从左往右看,第一个没有子节点的高度值,从左往右,当遇到第一个左孩子和右孩子都不存在的节点,那当前节点高度就是最小高度
  • 18
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值