夸父追日:第六章 二叉树part01

今日收获:二叉树的理论基础,三种遍历方式(递归遍历,迭代遍历,层序遍历)

1. 二叉树的理论基础

(1)不同的树:排序树就是搜索树

        二叉排序树:左节点的值<根节点<右节点的值,子树也是如此

        平衡二叉排序树:除了满足排序树要求之外,左右子树的高度差小于等于1

(2)Java中二叉树节点的定义

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;
    }
}

2. 递归遍历

(1)递归三要素:返回值和参数,确定递归的终止条件,单层递归的逻辑

(2)前序递归遍历:144. - 力扣(LeetCode)

class Solution {
    List<Integer> result = new ArrayList<>();

    public List<Integer> preorderTraversal(TreeNode root) {
        preOrder(root);
        return result;
    }

    // 返回值为空,传入遍历节点
    public void preOrder(TreeNode root){
        // 终止条件
        if (root==null){
            return;
        }

        // 单层递归
        result.add(root.val);
        preOrder(root.left);
        preOrder(root.right);
    }
}

(3)中序递归遍历和后序递归遍历:单层递归逻辑中改变递归左右子树与处理当前节点的顺序

中序递归遍历:94. - 力扣(LeetCode)

后序递归遍历:145. - 力扣(LeetCode)

3. 迭代遍历

(1)前序迭代遍历:144. - 力扣(LeetCode)

思路:利用栈存储当前节点及其左右孩子节点。当栈不为空时,弹出栈顶元素并记录值,先将右节点入栈,再让左节点入栈,这样出栈顺序就是中左右。

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> stack=new Stack<>();

        // 当前节点入栈,入栈节点必不为空
        if (root==null){
            return result;
        }
        stack.push(root);

        while (!stack.isEmpty()){
            TreeNode cur=stack.pop();
            result.add(cur.val);
            // 右孩子和左孩子入栈
            if (cur.right!=null){
                stack.push(cur.right);
            }
            if (cur.left!=null){
                stack.push(cur.left);
            }
        }
        return result;
    }
}

(2)中序迭代遍历:94. - 力扣(LeetCode)

思路:不断向栈中压入当前节点和当前节点的左孩子,直到当前节点为空时弹出栈顶元素并记录值,然后将指针指向当前节点的右孩子,进入下一轮循环

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 {
                cur=stack.pop();
                result.add(cur.val);
                cur=cur.right;
            }
        }
        return result;
    }
}

(3)后序迭代遍历:145. - 力扣(LeetCode)

思路:和前序遍历的左右子树入栈顺序相反,最后将结果数据反转。入栈顺序中左右,出栈是中右左,再反转就是左右中

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> stack=new Stack<>();

        if (root==null){
            return result;
        }
        stack.push(root);

        // 和前序迭代遍历左右孩子入栈顺序相反
        while (!stack.isEmpty()){
            TreeNode temp=stack.pop();
            result.add(temp.val);

            // 左右入栈
            if (temp.left!=null){
                stack.push(temp.left);
            }

            if (temp.right!=null){
                stack.push(temp.right);
            }
        }

        // 出栈顺序中右左反转
        Collections.reverse(result);
        return result;
    }

}

(4)迭代遍历统一写法

思路:三种遍历顺序按照和出栈相反的顺序入栈。在压入中节点时,额外压入一个空节点作为标记

前序统一迭代遍历:144. - 力扣(LeetCode)

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Stack<TreeNode> stack=new Stack<>();

        // 当前节点入栈,入栈节点必不为空
        if (root!=null){
            stack.push(root);
        }
        
        while (!stack.isEmpty()){
            TreeNode temp=stack.peek();
            if (temp!=null){
                stack.pop();  // 避免重复,下面要统一处理

                // 不是中节点
                if (temp.right!=null){
                    stack.push(temp.right);
                }

                if (temp.left!=null){
                    stack.push(temp.left);
                }

                stack.push(temp);
                stack.push(null);
            }else {
                // 遇到了中节点
                stack.pop(); // null
                TreeNode cur=stack.pop();
                result.add(cur.val);
            }
        }
        return result;
    }
}

中序统一迭代遍历:94. - 力扣(LeetCode)

后序统一迭代遍历:145. - 力扣(LeetCode)

4. 层序遍历:做一送十,关键是标记遍历的层级

题目连接:102. - 力扣(LeetCode)

递归思路:利用整数标记遍历的层数,左右子树传入相同的层级数

class Solution {
    List<List<Integer>> result=new ArrayList<>();
    
    public List<List<Integer>> levelOrder(TreeNode root) {
        sameLevel(root,0);
        return result;
    }

    public void sameLevel(TreeNode node,int deep){
        if (node==null){
            return;
        }
        deep++;

        // 新的一层列表
        if (result.size()<deep){
            List<Integer> item=new ArrayList<>();
            result.add(item);
        }

        // 当前节点加入所在列表
        result.get(deep-1).add(node.val);

        sameLevel(node.left,deep);
        sameLevel(node.right,deep);
    }
}

迭代思路:利用队列,首先将根节点加入队列,如果队列的大小不为空,首先记录本层级的元素个数,再不断弹出队头元素并加入其左右节点,本层级遍历结束再记录下一层级的元素个数。

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result=new ArrayList<>();
        Queue<TreeNode> queue=new LinkedList<>();

        if (root!=null){
            queue.offer(root);
        }

        while (!queue.isEmpty()){
            // 本层级的长度
            int len=queue.size();

            List<Integer> item=new ArrayList<>();  

            while (len>0){
                TreeNode temp=queue.poll();
                // 左右节点入队
                if (temp.left!=null){
                    queue.offer(temp.left);
                }

                if (temp.right!=null){
                    queue.offer(temp.right);
                }

                item.add(temp.val);
                len--;
            }

            result.add(item);
        }
        return result;
    }
}

107.二叉树的层次遍历II

思路:反转层序遍历的结果列表

199.二叉树的右视图

思路:每次在结果集中添加层序遍历中每层的最后一个元素

637.二叉树的层平均值

思路:层序遍历计算每一层的元素之和再求平均值

429.N叉树的层序遍历

思路:二叉树的左右子树入队变为子树列表入队

515.在每个树行中找最大值

思路:记录每一层的最大值

116.填充每个节点的下一个右侧节点指针

思路:层序迭代遍历,只要本层还有元素,就将弹出的元素指向队头元素

117.填充每个节点的下一个右侧节点指针II

同116的做法

104.二叉树的最大深度

思路:层序迭代遍历记录总的循环次数

111.二叉树的最小深度

思路:层序迭代遍历,找出层中的第一个叶子节点就返回深度

5. 大总结

(1)每一种遍历方式的递归法和迭代法必须都掌握,尤其是迭代法。 

(2)代码随想录中总结的递归三部曲:返回值和参数,终止条件,单层递归的逻辑

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值