【Java数据结构】二叉树的前中后序遍历(递归和非递归)

这篇具有很好参考价值的文章主要介绍了【Java数据结构】二叉树的前中后序遍历(递归和非递归)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

二叉树遍历是二叉树的一种重要操作 必须要掌握

二叉树的遍历可以用递归和非递归两种做法来实现

递归做法

前序遍历

前序遍历的遍历方式是先根节点 在左节点 在右节点
【Java数据结构】二叉树的前中后序遍历(递归和非递归)
述这棵树前序遍历的结果是:A B D E C F G

递归的思想就是把问题拆分成一个个小问题来解决

public void preOrder(treeNode root) {
        if (root == null) return;
        System.out.print(root.val + " ");
        preOrder(root.left);
        preOrder(root.right);
        //前序遍历 先打印根节点 在打印左节点 在打印右节点
        //将大问题拆分成小问题 递归解决
    }

treeNode是一个内部类 具体实现

public static class treeNode {
        treeNode left;
        //左节点
        treeNode right;
        //右节点
        int val;
        //值
        public treeNode(int val) {
            this.val = val;
        }
    }

中序遍历

中序遍历的顺序是先左 在根 在右

**递归实现的代码都十分相似 **

public void inOrder(treeNode root) {
        if (root == null) return;
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
		//和前序遍历相比 只是递归的顺序改变了
		//和遍历的顺序一样
		//先左 在根 在右
    }

后序遍历

后序遍历的顺序是先左 在右 在根

public void postOrder(treeNode root) {
        if (root == null) return;
        inOrder(root.left);
        inOrder(root.right);
        System.out.print(root.val + " ");
		//只是顺序发生了改变
    }

非递归

前序遍历

我们不采用递归的方式 但是要模拟递归的思路

非递归实现 我们要借用栈这个数据结构
【Java数据结构】二叉树的前中后序遍历(递归和非递归)
例如这颗树 我们用它举例

模拟递归 我们第一步肯定是先遍历左树 直到遍历到叶子节点
因为我们是前序遍历 所以每向下遍历一次就记录下来这个节点的值(可以是打印 也可以放在其他集合中)然后把这个节点放在栈中

【Java数据结构】二叉树的前中后序遍历(递归和非递归)
此时我们的cur指针指向了D节点的左子节点位置 此时发现cur现在为空 说明我们已经到达左树的最底部此时我们要看D节点有没有右子节点 因为我们的顺序是(根左右)

但是我们的cur已经指向了D节点的左子树,而且二叉树是单向的 我们怎么能找到D节点呢?

此时栈就起到了作用 因为我们每打印一次就把这个节点压入栈中 此时栈顶元素是D节点 我们可以弹出栈顶元素D 然后把弹出来的元素赋值给cur 在使cur指向D的右树 即

cur = stack.pop();
cur = cur.right;

然后在执行上述的操作遍历左树
根据上面的思想我们可以写出大体逻辑

public void preOrderNor(TreeNode root) {
        if(root == null) return;
        Stack<TreeNode> stack = new Stack<>();
        //创建一个栈来存放元素
        TreeNode cur = root;
        //定义指针

    
        //这个循环内就是我们刚才的思路
            while (cur != null) {
                stack.push(cur);
                System.out.print(cur.val + " ");
                cur = cur.left;
            }//这个循环就是一直遍历左数直到左子树被遍历完
            TreeNode top = stack.pop();
            cur = top.right;
            //弹出栈顶元素 cur赋值栈的右子树
        
    }

但是我们可以看到 我们还没有分析出来最外层循环的出口 即什么时候这棵二叉树就完成了遍历?

首先我们要思考一个问题 当cur等于D的右子节点的时候 cur此时需不需要入栈? 答案是肯定需要的 那么 我们入栈的代码应该怎么写呢?

因为我们入栈后 还需要继续遍历左子树直到遍历完全部的左节点 所以这里肯定是一个循环 我们需要在这个循环的外部在嵌套一层循环 那么我们循环的条件就可以写成cur!=null 因为只有!=null时 内层的循环才可以执行 也就是才可以入栈

public void preOrderNor(TreeNode root) {
        if(root == null) return;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;

        while (cur != null) {
            while (cur != null) {
                stack.push(cur);
                System.out.print(cur.val + " ");
                cur = cur.left;
            }
            //cur == null
            TreeNode top = stack.pop();
            cur = top.right;
        }
    }

**这段代码还有一个问题 我们外层的循环条件是cur!=null 但是我们在cur获取到D的右子节点时 此时cur又为空了 此时循环条件不满足 循环就结束了 但是我们还没有遍历完这棵二叉树 我们还有什么依据来继续循环呢? **

此时就要用到我们的栈 此时栈中还有元素 说明二叉树还没有遍历完 所以循环条件还要加栈不为空才可以

public void preOrderNor(TreeNode root) {
        if(root == null) return;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;

        while (cur != null || !stack.empty()) {
            while (cur != null) {
                stack.push(cur);
                System.out.print(cur.val + " ");
                cur = cur.left;
            }
            //cur == null
            TreeNode top = stack.pop();
            cur = top.right;
        }
    }

我们加了这个条件之后 此时进入循环 cur = top.right 此时栈顶元素是B节点 cur此时指向B节点的右子节点 此时整棵树就可以串起来 遍历结束了

此时我们的二叉树的前序非递归遍历就结束了

中序遍历

中序遍历的思路和前序遍历基本一致 只需要改变打印的位置即可
前序遍历我们因为要先打印根节点 所以在找左子树的最后一个节点的位置时 每次循环都打印 这样就可以先打印出来根节点

中序遍历需要先打印左节点 在打印根节点 所以需要们在最后到了底部的时候再去打印节点即可 其他代码不需要改变

public void preOrderNor(TreeNode root) {
        if(root == null) return;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;

        while (cur != null || !stack.empty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            //cur == null
            TreeNode top = stack.pop();
            System.out.print(cur.val + " ");
            cur = top.right;
        }
    }

后序遍历

后续遍历的情况比上面两种情况就更复杂
在上面的代码的主体框架下 要对更多情况进行考虑

首先不能再遍历左子树的时候就进行打印操作 不管是到树的底部打印还是每遍历一次就打印一次 因为后序遍历是先左 后右 在根节点

我们再走到左子树的最底部的左节点时 也就是下图的情况
【Java数据结构】二叉树的前中后序遍历(递归和非递归)
cur指向D节点的左子节点 此时我们还要判断他有没有右子节点 因为右子节点的打印优先级高于根节点 如果右节点也为空 此时在打印D

且我们还需要注意的一点是 前序和中序遍历时都只需要判断左树是否为空 所以压入栈的元素只需要出一次栈即可完成目的
但是后序遍历我们不仅要判断左树是否为空 还需要判断右树是否为空 如果在判断左树时就把栈顶元素弹出 在判断右树是否为空是 就没有元素判断 所以在判断左树是否为空时 只需要peek元素 在判断完右树的元素时 在弹出元素

代码大致如下

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.empty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            if(top.right == null){
                ret.add(top.val);
                stack.pop();
            }else{
                cur = top.right;
            }
        }
        return ret;
    }
}
//这里没有打印元素 而是将元素放进一个集合中 和打印思路相同

此时代码还有一点点小问题

根据上面的思路写出代码 我们根据代码遍历一次
当cur = D 时 此时cur不为空 cur = D的左节点 此时cur为空 peek出栈顶元素D 检查D的右树是否为空 发现是空 此时将D的值放入一个集合 把D弹出栈

遍历到这步还没有问题 当我们在进入循环cur还是= D 又进行了刚才 的操作 我们发现在这里出现了死循环 只能在D这个节点处遍历

根本问题就是 我们已经打印过D了 所以D不需要进入判断 所以判断条件不仅是top.
right == null 在这个节点打印过的时候 也应该进入else

代码如下文章来源地址https://www.toymoban.com/news/detail-426082.html

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while(cur != null || !stack.empty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            if(top.right == null || top.right == prev){
                prev = top;
                ret.add(top.val);
                stack.pop();
            }else{
                cur = top.right;
            }
        }
        return ret;
    }
}

到了这里,关于【Java数据结构】二叉树的前中后序遍历(递归和非递归)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 初阶数据结构之---二叉树链式结构(二叉树的构建,二叉树的前序,中序,后序和层序遍历,计算二叉树结点个数,第k层结点个数,叶子结点个数,判断是否为完全二叉树)

    本篇博客是初阶数据结构树的收尾,将会讲掉 基本二叉树链式结构的具体内容和实现,包括二叉树的构建,前序遍历,中序遍历,后序遍历和层序遍历,计算二叉树结点个数,第k层结点个数,二叉树叶子结点个数,以及判断一个二叉树是否为完全二叉树 。话不多说,开始我

    2024年03月24日
    浏览(34)
  • Java数据结构——二叉树的遍历

     作者:敲代码の流川枫 博客主页:流川枫的博客 专栏:和我一起学java 语录:Stay hungry stay foolish 工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器——牛客网 点击注册和我一起刷题 文章目录 1.创建二叉树 2.二叉树的三种遍历方式 3.代码实现遍历 前序遍历

    2024年01月22日
    浏览(32)
  • Java 数据结构篇-二叉树的深度优先遍历(实现:递归方式、非递归方式)

    🔥博客主页: 【 小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍    文章目录         1.0 二叉树的说明         1.1 二叉树的实现         2.0 二叉树的优先遍历说明         3.0 用递归方式实现二叉树遍历         3.1 用递归方式实现遍历 - 前序遍历         3.2 用递归

    2024年02月05日
    浏览(38)
  • Java学数据结构(2)——树Tree & 二叉树binary tree & 二叉查找树 & AVL树 & 树的遍历

    1.树的出现:解决链表线性访问时间太慢,树的时间复杂度O(logN); 2.二叉树的定义,最多两个儿子节点; 3.二叉查找树,左小,右大,中居中;remove方法,两种,只有一个儿子节点,有两个儿子节点; 4.AVL树,在二叉查找树基础上加平衡条件,旋转方法,单旋转,双旋转;

    2024年02月10日
    浏览(40)
  • 【数据结构】树、二叉树的概念和二叉树的顺序结构及实现

    之前我们学习了顺序表、链表以及栈和队列这些数据结构,但这些数据结构都是线性的(一对一)。接下来要学习 非线性的数据结构——树(二叉树) ,相比前面的,树的结构更加复杂,话不多说,直接进入正题吧。 树是一种 非线性的数据结构 ,它是 一对多(也有可能是

    2024年02月07日
    浏览(29)
  • 数据结构:二叉树的链式结构

    朋友们、伙计们,我们又见面了,本期来给大家解读一下链式二叉树的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! 数据结构与算法专栏 :数据结构与算法 个  人  主  页 :stackY、 C 语 言 专 栏 :C语言:从入门到精通 目录 前言

    2024年02月07日
    浏览(45)
  • 数据结构——二叉树的链式结构

      个人主页 : 日刷百题 系列专栏 : 〖C语言小游戏〗〖Linux〗〖数据结构〗  〖C语言〗 🌎 欢迎各位 → 点赞 👍+ 收藏 ⭐️+ 留言 📝  ​ 这里我们使用先序遍历的思想来创建二叉树,这里的内容对于刚接触二叉树的朋友可能有些难理解,不妨先看完下面的二叉树各种遍历

    2024年02月05日
    浏览(34)
  • 【数据结构】二叉树的链式结构

    学习链式二叉树要知道三种遍历方式,便于对二叉树的节点以及左子树和右子树进行操作。 前序遍历:根、左子树、右子树 中序遍历:左子树、根、右子树 后序遍历:左子树、右子树、根 以下图为例: 得到的结果: 前序遍历:1 2 3 4 5 6 中序遍历:3 2 1 5 4 6 后序遍历:3 2

    2024年02月08日
    浏览(37)
  • 【数据结构】二叉树的介绍和二叉树堆

    💓作者简介: 加油,旭杏,目前大二,正在学习 C++ , 数据结构 等👀 💓作者主页:加油,旭杏的主页👀 ⏩本文收录在:再识C进阶的专栏👀 🚚代码仓库:旭日东升 1👀 🌹欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖        树这一概念,在我们刚开始听说的时候会觉得

    2024年01月20日
    浏览(40)
  • 【数据结构】树,二叉树,满二叉树,完全二叉树的定义和二叉树的基本操作

    🎊专栏【数据结构】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【勋章】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 目录 ⭐树 🏳️‍🌈定义  🏳️‍🌈注意 🍔树的基本术语 ⭐二叉树 🏳️‍🌈定义 🎆二叉树和树的区别 🏳️‍🌈二叉树

    2024年02月05日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包