【代码随想录训练营】Day14-二叉树

代码随想录训练营 Day14

今日任务

二叉树的理论基础
二叉树的递归遍历
二叉树的迭代遍历
二叉树的统一迭代遍历
语言:Java

二叉树的理论基础

分类

  1. 满二叉树:只有度为0和度为2的节点,且度为0的节点在同一层上
  2. 完全二叉树:只有最后一层节点没填满
  3. 二叉搜索树:左节点 < 根节点 < 右节点
  4. 平衡二叉搜索树:AVL树,在二叉搜索树的基础上,保证是平衡的(左右子树高度差不超过1)

存储方式

  1. 链式存储
  2. 顺序存储

遍历方式

  1. 深度优先遍历:
    前序遍历:递归法,迭代法
    中序遍历:递归法,迭代法
    后序遍历:递归法,迭代法
  2. 广度优先遍历:
    层序遍历:迭代法

二叉树的递归遍历

递归三步走:

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

前序遍历
遍历顺序:根-左-右
链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/

/**
 * 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 void Traversal(TreeNode node, List<Integer> result){
        if(node == null){
            return;
        }
        result.add(node.val); //根
        Traversal(node.left, result); //左
        Traversal(node.right, result); //右
    }
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Traversal(root, result);
        return result;
    }
}

中序遍历
遍历顺序:左-根-右
链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/

/**
 * 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 void Traversal(TreeNode node, List<Integer> result){
        if(node == null){
            return;
        }
        Traversal(node.left, result); //左
        result.add(node.val); //根
        Traversal(node.right, result); //右
    }
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Traversal(root, result);
        return result;
    }
}

后序遍历
遍历顺序:左-右-根
链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/

/**
 * 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 void Traversal(TreeNode node, List<Integer> result){
        if(node == null){
            return;
        }
        Traversal(node.left, result); //左
        Traversal(node.right, result); //右
        result.add(node.val); //根
    }
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Traversal(root, result);
        return result;
    }
}

二叉树的迭代遍历

前序遍历
遍历顺序:根-左-右
链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        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;
    }
}

中序遍历
遍历顺序:左-根-右
难点:节点访问顺序和处理顺序不同
不同点:中序遍历的写法不像前序遍历和后序遍历一样会在每次循环开始就更新cur的值,而是需要自己在 if 和 else 中手动更新cur的值
链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode cur = root;
        //cur != null 主要用于第一次进入循环
        while(!stack.isEmpty() || cur != null){
            //找到最左元素
            if(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            //此时cur必为最左元素,将最左元素加入result
            //将最左元素的右子树赋给cur,按同样规则遍历右子树
            else{
                cur = stack.pop();
                result.add(cur.val);
                cur = cur.right;
                //错误一
                // if(cur.right != null){
                // 	result.add(cur.right.val);
                // }
                //错误二
                // if(cur.right != null){
                // 	stack.push(cur.right);
                // }
                //两种错误写法都会导致cur不断将相同元素压入result/stack中,导致内存溢出
            }
        }
        return result;
    }
}

后序遍历
在前序遍历的基础上修改部分代码
遍历顺序:左-右-根
写法:根-左-右——>(调整代码顺序)根-右-左——>(反转)左-右-根
链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/

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

二叉树的统一迭代遍历

关键:用null作为访问过但未处理过的根节点的标记
前序遍历
遍历顺序:根-左-右
入栈顺序:右-左-根
链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if(root == null){
            return result;
        }
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode cur = stack.peek();
            if(cur != null){
                stack.pop();
                if(cur.right != null){
                    stack.push(cur.right); //右
                }
                if(cur.left != null){
                    stack.push(cur.left); //左
                }
                stack.push(cur);
                stack.push(null); //根
            }
            else{
                stack.pop();
                cur = stack.pop();
                result.add(cur.val);
            }
        }
        return result;
    }
}

中序遍历
遍历顺序:左-根-右
入栈顺序:右-根-左
链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if(root == null){
            return result;
        }
        stack.push(root);
        while(!stack.isEmpty()){
            //TreeNode cur = stack.pop(); //这里不可以直接pop,否则本应该在else分支里pop的null在这里就直接被pop了
            TreeNode cur = stack.peek();
            if(cur != null){ //null是用来标记根节点的,所以要保证除了自己手动加的null外没有其他的null入栈
                stack.pop();
                if(cur.right != null){
                    stack.push(cur.right); //右
                }
                stack.push(cur); //根
                stack.push(null);
                if(cur.left != null){
                    stack.push(cur.left); //左
                }
            }
            else{
                stack.pop(); //弹出null,准备处理根节点
                cur = stack.pop();
                result.add(cur.val);
            }
        }
        return result;
    }
}

后序遍历
遍历顺序:左-右-根
入栈顺序:根-右-左
链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if(root == null){
            return result;
        }
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode cur = stack.peek();
            if(cur != null){
                stack.push(null); //根
                if(cur.right != null){
                    stack.push(cur.right); //右
                }
                if(cur.left != null){
                    stack.push(cur.left); //左
                }
            }
            else{
                stack.pop();
                cur = stack.pop();
                result.add(cur.val);
            }
        }
        return result;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值