算法基础之数据结构——二叉树

本文详细介绍了二叉树的遍历方法,包括前序、中序、后序遍历的递归和迭代实现,以及层序遍历。同时讲解了如何利用递归和迭代解决二叉树问题,如最大深度、对称二叉树和路径总和等。最后总结了从遍历序列构造二叉树的方法。
摘要由CSDN通过智能技术生成

数据结构之二叉树——LeetCode笔记

  • 树的每个节点有一个值、包含所有子节点的列表

  • 二叉树的每个节点最多有两个子树——左子树、右子树

一、树的遍历

1、介绍

  • ① 前序遍历:先访问根节点,然后遍历左子树,最后遍历右子树

在这里插入图片描述

在这里插入图片描述

  • ② 中序遍历:先遍历左子树,然后访问根节点,最后遍历右子树

在这里插入图片描述
在这里插入图片描述

  • 后序遍历:先遍历左子树,然后遍历右子树,最后访问树的根节点

在这里插入图片描述
在这里插入图片描述
小结

  • 前序:根左右;中序:左根右;后序:左右根;
  • 通过中序遍历得到一个递增的有序序列
  • 后序可用于数学中的后缀表示法,结合栈处理表达式:遇到一个操作符,就可以从栈中弹出栈顶的两个元素,计算并将结果返回到栈中

2、二叉树的前序遍历 (LeetCode 144

方法1——递归

  • 首先判断当前节点(默认其是根节点),是否存在
  • 若当前节点存在,则加入列表;然后依次递归当前节点的左子树、右子树
  • 若不存在则返回
class Solution {
   
    List<Integer> res = new ArrayList<Integer>();

    public List<Integer> preorderTraversal(TreeNode root) {
   
        if(root == null) return res;
        res.add(root.val);
        if(root.left != null) preorderTraversal(root.left);
        if(root.right != null) preorderTraversal(root.right);
        return res;
    }
}

class Solution {
   
    public List<Integer> preorderTraversal(TreeNode root) {
   
        // 创建结果数组
        List<Integer> res = new ArrayList<Integer>();
        // 前序遍历方法
        preOrder(root, res);
        return res;

    }

    public void preOrder(TreeNode root, List<Integer> res){
   
        // 若当前节点不存在,则返回
        if(root == null)    return ;
        // 当前节点存在,将值存入列表
        res.add(root.val);
        // 先左、后右地递归
        preOrder(root.left, res);
        preOrder(root.right, res);
    }
}

方法2——迭代:使用栈存储数据

  • 按照根左右的顺序查询
  • 若当前节点存在,则将该节点压入栈
  • 若当前节点不存在,则将栈顶的一个弹出来
class Solution {
   
    public List<Integer> preorderTraversal(TreeNode root) {
   
        // 创建结果数组
        List<Integer> res = new ArrayList<Integer>();
        if(root == null) return res;
        Deque<TreeNode> stack = new LinkedList<TreeNode>();
        TreeNode cur = root;
        while(!stack.isEmpty() || cur != null){
   
            while(cur != null){
   
                res.add(cur.val);
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            cur = cur.right;    // 因为根和左子树的值都已经加入列表了
        }
        return res;
    }   
}

方法3——Morris遍历

  • 假设先将所有node.right打断(实际编程不用,图解示意而已)

在这里插入图片描述

  • 对于某个节点cur,找到其左子树中最右侧末端的节点,将其 “插入” 连接原本cur.right指向的节点;若左子树无右侧节点,则将左子树(节点)插入;若无左子树,跳过,如:
    • 对于B节点,A是左子树,但是A没有任何左子树(节点)或右子树(节点),直接将A插入B.right,原本指向的D节点前面
    • 对于D节点,则是将C插入D.right,E节点前面
    • 对于F节点,左子树的最右侧末端节点是E,则将E插入F.right,G节点前面
    • 对于G节点,无左子树,则跳过
    • 最后,根据连接线的顺序,遍历即为结果

在这里插入图片描述

  • 【注】由于并不是真的“打断”,而最右侧末端节点连接cur节点时,在debug过程会出现成环错误Error - Found cycle in the TreeNode
class Solution {
   
    public List<Integer> preorderTraversal(TreeNode root) {
   
        List<Integer> res = new ArrayList<Integer>();
        if(root == null) return res;

        TreeNode cur = root, temp = null;

        while(cur != null){
   
            temp = cur.left;    // cur节点的左子树首个节点
            if(temp != null){
   
                while(temp.right != null && temp.right != cur){
   
                    temp = temp.right;  // 一直达到右侧最末端节点
                }
                if(temp.right == null){
   
                    res.add(cur.val);
                    temp.right = cur;	// Error - Found cycle in the TreeNode
                    cur = cur.left;
                    continue;
                } else {
   
                    temp.right = null;
                }
            } else {
   
                res.add(cur.val);
            }
            cur = cur.right;
        }
        return res;
    }
}

3、二叉树的中序遍历 (LeetCode 94

方法1——递归

class Solution {
   
    List<Integer> res = new ArrayList<Integer>();
    
    public List<Integer> inorderTraversal(TreeNode root) {
       
        if(root == null) return res;
        
        if(root.left != null) inorderTraversal(root.left);
        res.add(root.val);
        if(root.right != null) inorderTraversal(root.right);

        return res;
    }
}

class Solution {
   
    public List<Integer> inorderTraversal(TreeNode root) {
   
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }

    public void inorder(TreeNode root, List<Integer> res){
   
        if(root == null) return ;

        if(root.left != null) inorder(root.left, res);
        res.add(root.val);
        if(root.right != null) inorder(root.right, res);
    }
}

方法2——迭代

class Solution {
   
    public List<Integer> inorderTraversal(TreeNode root) {
   
        List<Integer> res = new ArrayList<Integer>();
        Deque<TreeNode> stack = new LinkedList<TreeNode>();

        while(!stack.isEmpty() || root != null){
   
            while(root != null){
   
                stack.push(root);
                root = root.left;    
            }
            root = stack.pop();
            res.add(root.val);
            root = root.right;
        }

        return res;
    }
}

方法3——Morris遍历

  • 优点:没有使用任何辅助空间
  • 缺点:改变了整个树的结构,强行把二叉树改成链表
  • 步骤:
    • ① 判断当前节点cur的左节点cur.left,是否存在

在这里插入图片描述
- ② 若cur.left存在,则当前节点cur以及连带cur的全部右子树,挂在cur.left的最右侧节点下
在这里插入图片描述
在这里插入图片描述
- ③ 若cur.left为空了,则输出这个节点,向右遍历
- ④ 重复以上操作

class Solution {
   
    public List<Integer> inorderTraversal(TreeNode root) {
   
        List<Integer> res = new ArrayList<Integer>();

        TreeNode prev = null;
        while(root != null){
   
            if(root.left != null){
   
                prev = root.left;   // 判断当前节点的左子树是否存在
                // 若存在,直接指向左子树最最右侧的节点
                while(prev.right != null) prev = prev.right;
                // 将当前节点及其左、右子树连接到左子树最最右侧节点的右侧
                prev.right = root;
                
                TreeNode temp = root;
                // 将当前节点指向左子树
                root = root.left;
                // 将之前连接的一团中,其左子树“砍掉”
                temp.left = null;
            } else {
   
                res.add(root.val);
                root = root.right;
            }
        }

        return res;
    }
}

图解
在这里插入图片描述
在这里插入图片描述

4、二叉树的后序遍历 (LeetCode 145

方法1——递归

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

    public List<Integer> postorderTraversal(TreeNode root) {
   
        if(root == null) return res;
        if(root.left != null) postorderTraversal(root.left);
        if(root.right != null) postorderTraversal(root.right);
        res.add(root.val);
        return res;
    }
}

class Solution {
   
    public List<Integer> postorderTraversal(TreeNode root) {
   
        List<Integer> res = new ArrayList<Integer>(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值