二叉树遍历小结

article/2025/10/8 17:44:23

前言

二叉树是相当重要的数据结构,目前我还只会玩玩它的遍历(年轻不懂事没好好学,不然早就达到人生巅峰了),LeetCode上二叉树的简单题,大部分通过遍历加一点小逻辑即可解决,所以总结一下几种遍历方法(其实也是看题解白嫖的)。
二叉树遍历有广度优先,深度优先两种方式,深度优先又分先序遍历(根,左,右),中序遍历(左,根,右),后序遍历(左,右,根),如果是二叉搜索树,中序遍历就是有序的了。广度优先方式没太多说,只能借助队列实现,而深度优先,可通过递归方式,借助栈迭代方式,还有一种巧妙的莫里斯算法,空间复杂度是O(1),但莫里斯算法应该会改变树的结构。

广度优先遍历

首先将根结点入队,然后只要队列非空,就出队一个元素,然后只要子节点不为空就入队,队列为空时,就是遍历完成时

public int bfs(TreeNode root) {Queue<TreeNode> queue=new LinkedList<TreeNode>();if(null!=root){queue.add(root);}int depth=0;while(!queue.isEmpty()){depth++;int size = queue.size();					//这两行不要也可以,按queue不为空就可以完成遍历for(int i=0;i<size;i++){					//size可以控制一层一次循环,可以获取当前的高度TreeNode curNode=queue.poll();if(null!=curNode.left){queue.add(curNode.left);}if(null!=curNode.right){queue.add(curNode.right);}}}return depth;
}

深度优先遍历

递归方式

递归方式代码很简洁,调整一下顺序前中后序遍历也都出来了。然后不断地嵌套函数调用,不管哪种语言每次函数调用操作系统都需要压栈,JVM中每次方法调用也是需要创建栈帧,如果数据量大会消耗大量内存,肯定会抛出StackOverflowError

1.先序
public void dfs(TreeNode root) {if(null==root){return;}System.out.println(root.val);dfs(root.left);dfs(root.right);
}2.中序
public void dfs(TreeNode root) {if(null==root){return;}dfs(root.left);System.out.println(root.val);dfs(root.right);
}
根右左遍历
public void dfs(TreeNode root) {if(null==root){return;}dfs(root.right);System.out.println(root.val);dfs(root.left);
}3.后序
public void dfs(TreeNode root) {if(null==root){return;}dfs(root.left);dfs(root.right);System.out.println(root.val);
}

迭代方式(先序)

深度优先先序迭代遍历,先将根节点入栈,然后只要栈非空,就出栈一个元素,这时需要将右子节点先入栈,左子节点后入栈,这样下一次循环出栈的是左子节点,再继续下去就达到了目的

public List<Integer> dfs(TreeNode root) {List<Integer> res=new LinkedList<>();if(null==root){return res;}Stack<TreeNode> stack = new Stack<>();stack.push(root);					//入栈根结点while(!stack.isEmpty()){TreeNode curNode=stack.pop();	//出栈最后加入结点res.add(curNode.val);if(null!=curNode.right){stack.push(curNode.right);	//先入栈右子结点,后出栈右结点}if(null!=curNode.left){stack.push(curNode.left);	//后入栈左子结点,先出栈左子结点}}return res;
}

迭代方式(中序)

深度优先中序迭代遍历,每次循环需要将根节点的所有左结点全部入栈,然后出栈最后一个左子结点,即第一个结点,如果该结点有右子结点,将根节点赋值为该右子结点,接着下一次循环,这样又会把右子树入栈,遍历完后会回到第一次循环的父节点,栈为空后达到目的

public List<Integer> dfs(TreeNode root) {List<Integer> res=new LinkedList<>();if(null==root){return res;}Stack<TreeNode> stack = new Stack<>();while(!stack.isEmpty()||null!=root){while(null!=root){				//如果左子结点不为空,一值入栈左子结点stack.push(root);root=root.left;}TreeNode curNode=stack.pop();	//弹出最后一个左子结点res.add(curNode.val);if(null!=curNode.right){root=curNode.right;			//右子结点不为空,先处理右字树}}return res;        
}

迭代方式(后序)

深度优先后序迭代遍历,每次循环需要将根节点的所有左结点全部入栈,然后出栈最后一个左子结点,如果存在右孩子,入栈当前节点,指针移到右孩子,进入下一轮循环,一样全部入栈左子结点,当右子结点为空,加入遍历结果集,这是将指针值为空并将前驱结点置为此节点(前驱结点就是用于处理有右孩子的结点),下一轮循环,如果结点的右孩子等于前驱结点,也可以加入结果,一直循环到栈为空指针为空,遍历完成

public List<Integer> dfs(TreeNode root) {List<Integer> res=new LinkedList<>();if(null==root){return res;}Stack<TreeNode> stack = new Stack<>();TreeNode pre=null;								//前驱结点TreeNode curNode=root;while(!stack.isEmpty()||null!=curNode){while(null!=curNode){						//左子结点一直入栈stack.push(curNode);curNode=curNode.left;}curNode=stack.pop();						//出栈最后结点if(null==curNode.right||curNode.right==pre){	//pre为有右子节点的结点的右结点,用于处理有右孩子的父结点res.add(curNode.val);pre=curNode;curNode=null;}else{							//如果右子结点不为空,入栈右子结点,下轮循环处理右字树stack.push(curNode);curNode=curNode.right;}}return res;
}

莫里斯算法

莫里斯算法用巧妙的方式,将迭代遍历的空间复杂度降低为O(1),代码看起来没有多太多,但是理解却是需要更多的脑容量,这里借用了https://blog.csdn.net/yangfeisc/article/details/45673947的图解,有助于理解。

莫里斯先序

1 如果当前结点没有左子树,输出结点,当前结点指向右子结点,遍历右子树
2 如果当前结点有左子树,找到该左字树的最右子结点,如果最右子结点的右孩子为空,代表为根节点,输出结点值,如果最右子结点的右孩子不为空,当前移动到右孩子
3 当前结点为空,结束循环
莫里斯先序图解

public List<Integer> morris(TreeNode root) {List<Integer> res=new LinkedList<>();if(null==root){return res;}TreeNode cur = root;while(null!=cur){if(null==cur.left){					//用于处理最左子结点,最右子结点res.add(cur.val);cur=cur.right;					//回到后继根节点}else{TreeNode pre = cur.left;while(null!=pre.right&&pre.right!=cur){	 //找到左子结点的最右孩子,也就是根节点的前驱pre=pre.right;}if(null==pre.right){		//用于遍历其他结点,根节点,非最左最后res.add(cur.val);pre.right=cur;			//用于将前驱指向根节点	cur=cur.left;}else{					pre.right=null;cur=cur.right;				//用于回到根节点,或者移动到右子结点}}}return res;
}

莫里斯中序

1 如果当前结点没有左子树,输出结点,当前结点指向右子结点,遍历右子树
2 如果当前结点有左子树,找到该左字树的最右子结点,如果最右子结点不为空,代表为最左结点,输出结点值,然后回到父节点,或者右子结点
3 当前结点为空,结束循环
莫里斯中序图解

public List<Integer> morris(TreeNode root) {List<Integer> res=new LinkedList<>();if(null==root){return res;}TreeNode cur=root;while(null!=cur){if(null==cur.left){						//用于处理最左子结点,最右子结点res.add(cur.val);cur=cur.right;}else{TreeNode pre = cur.left;			//找到左子结点的最右孩子,即根节点的前驱结点while(null!=pre.right&&pre.right!=cur){pre=pre.right;}if(null==pre.right){				//用于将前驱指向根节点		pre.right=cur;cur=cur.left;}else{						res.add(cur.val);			//用于处理其他结点,左节点pre.right=null;cur=cur.right;				//用于回到根节点,或者移动到右子结点}}}return res;
}

莫里斯后序

莫里斯后序图解

public List<Integer> morris(TreeNode root) {List<Integer> res=new LinkedList<>();if(null==root){return res;}            TreeNode temp =new TreeNode();temp.left=root;TreeNode cur =temp;while(null!=cur){if(null==cur.left){// res.add(cur.val);				//最左子结点与最右子结点不再处理cur=cur.right;}else{TreeNode pre=cur.left;while(null!=pre.right&&pre.right!=cur){			//找到最右子结点,于根节点建立前驱联系pre=pre.right;}if(null==pre.right){pre.right=cur;								cur=cur.left;}else{pre.right=null;								//清掉前驱联系,重建二叉树TreeNode curTemp=cur.left;LinkedList<Integer> tempList=new LinkedList<>();		//处理所有结点,左子结点只处理一个,根节点与右子结点反向加入链表while(null!=curTemp){tempList.addFirst(curTemp.val);curTemp=curTemp.right;}res.addAll(tempList);						//后全部加入遍历集合cur=cur.right;}}}return res;
}

http://chatgpt.dhexx.cn/article/F1jTcF7a.shtml

相关文章

二叉树遍历之层次遍历算法入门详解

一、引言 二叉树的遍历常见的方法有先序遍历、中序遍历、后序遍历和层次遍历等&#xff0c;本文给出了C语言版本的层次遍历二叉树的算法。 层次遍历的原理很简单&#xff0c;总结为一句话就是“从上到下&#xff0c;从左到右”&#xff0c;就是从树根开始逐层访问二叉树的结点&…

二叉树的四种遍历算法

二叉树作为一种重要的数据结构&#xff0c;它的很多算法的思想在很多地方都用到了&#xff0c;比如STL算法模板&#xff0c;里面的优先队列、集合等等都用到了二叉树里面的思想&#xff0c;先从二叉树的遍历开始&#xff1a; 看二叉树长什么样子&#xff1a; 我们可以看到这颗…

实现二叉树各种遍历算法

目录 前言一、题目1.二叉树的各种遍历过程及遍历算法设计。2.实现二叉树各种遍历算法 总结 前言 提示&#xff1a;记得关注我哦&#xff01;&#xff01;&#xff01; 一、题目 1.二叉树的各种遍历过程及遍历算法设计。 &#xff08;1&#xff09; 先序遍历二叉树&#xff1…

算法分析之二叉树遍历

算法相关数据结构总结&#xff1a; 序号数据结构文章1动态规划动态规划之背包问题——01背包 动态规划之背包问题——完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 算法&#xff08;Java&#xff09;——动态规划2数组算法分析之数…

二叉树遍历算法总结

A. 二叉树的遍历 1.前序遍历二叉树&#xff1a; (1)若二叉树为空&#xff0c;则为空操作&#xff0c;返回空。 (2)访问根结点。 (3)前序遍历左子树。 (4)前序遍历右子树。 a.二叉树前序遍历的递归算法&#xff1a; void PreOrderTraverse(BiTree BT)…

二叉树的遍历算法

遍历是对树的一种最基本的运算&#xff0c;所谓遍历二叉树&#xff0c;就是按一定的规则和顺序走遍二叉树的所有节点&#xff0c;使每一个节点都被访问一次&#xff0c;而且只被访问一次。由于二叉树是非线性结构&#xff0c;因此&#xff0c;树的遍历实质上是将二叉树的各个节…

【算法】二叉树遍历算法总结:前序中序后序遍历

前言 二叉树遍历是非常经典的算法题&#xff0c;也是二叉树的一道基础算法题。 但是在平常的笔试面试中&#xff0c;其出现的频率其实并不是特别的高&#xff0c;我推测是这种题目相对来说比较基础&#xff0c;算是一个基础知识点。 比如剑指offer中出现的后序遍历题目&…

二叉树遍历算法的应用

目录 一、二叉树遍历算法的应用——二叉树的创建 二、二叉树遍历算法的应用——复制二叉树 三、二叉树遍历算法的应用——计算二叉树的深度 四、二叉树遍历算法的应用——计算二叉树节点总数 五、二叉树遍历算法的应用——计算二叉树叶子节点数 一、二叉树遍历算法的应用—…

一文弄懂二叉树的三种遍历方式

关注公众号【高性能架构探索】&#xff0c;后台回复【pdf】&#xff0c;免费获取计算机必备经典书籍 俗话说&#xff1a;学如逆水行舟,不进则退;心似平原走马,易放难收。这句话对程序员而言&#xff0c;体会更深。这行已经越来越卷了&#xff0c;时刻准备着&#xff0c;&#x…

二叉树遍历算法

目录 先序遍历 中序遍历 后序遍历 层序遍历 938. 二叉搜索树的范围和 110. 平衡二叉树 114. 二叉树展开为链表 117. 填充每个节点的下一个右侧节点指针 II 116. 填充每个节点的下一个右侧节点指针 1&#xff0c;三种遍历都是先把二叉树的最左结点循环入栈(DFS迭代)&am…

二叉树的四种遍历算法(结构体数组)

一、二叉树的定义 以字符串的形式定义一棵二叉树的先序序列&#xff0c;若字符是‘#’&#xff0c;表示该二叉树是空树&#xff0c;否则该字符是相应结点的数据元素。 例&#xff1a;ABDG##HI####CE#J##F## 对应的二叉树&#xff1a; 思路讲解&#xff1a; 想要遍历二叉树&am…

二叉树的四种遍历方式

概要 树本身是一种简单化的图 &#xff1b; DFS对应前中后序遍历&#xff0c;BFS对应层序遍历 二叉树结构 struct treenode {int val;treenode *left;treenode *right;treenode() : val(0), left(nullptr), right(nullptr) {}treenode(int x) : val(x), left(nullptr), right(n…

针对Linux学习,值得阅读的五本书籍,不看可能错失机会

今天为了总结一些可以帮助各位在学习过程中提供帮助的一些书籍。 一.鸟叔的私房菜&#xff1a; 本书是知名度颇高的Linux入门书《鸟哥的Linux私房菜基础学习篇》的新版,而详细地介绍了Linux操作系统。全书分为五部分;第一部分部分着重说明计算机的基础知识、Linux的学习方法&a…

从零开始学习Linux(一)关闭虚拟机系统

关闭系统&#xff0c;需要输入如下命令 poweroff然而&#xff0c;你只能得到如下反馈 -bash: poweroff:command not found此项错误是因为poweroff命令是一个系统管理命令。执行此项命令需要高级使用者特权。 因此&#xff0c;为了关闭系统&#xff0c;我们首先需要切换到root…

个人随笔/小白应该如何学习Linux,我的一些心得分享.

大家好&#xff0c;今天给大家分享一下0基础的人如何入门Linux&#xff0c;此文来源&#xff1a;我在上班的路上看到一篇文章&#xff0c;也是写的0基础的人如何学习Linux的文章。当时我在想&#xff0c;我写博文一年多&#xff0c;都是相关Linux及Python等技术的文章&#xff…

Linux学习路线

&#xfeff;&#xfeff; 关于 Linux Linux 因其开源&#xff0c;免费&#xff0c;可裁剪&#xff0c;被应用到很多领域&#xff0c;尤其是嵌入式设备上。 Android 系统内核也是基于 Linux 的。 另外还有各种服务器和工作站也是用的 Linux。 什么是嵌入式设备&#xff1f;…

为什么要学习 Linux ????

目前企业中大量的使用Linux作为服务器&#xff0c;在以后你们就业后&#xff0c;会发现web服务器Tomcat ,jobss这一类都是搭建在linux上面的&#xff0c;后面我们需要学习的数据库mysql &#xff0c; oracle &#xff0c;db2&#xff0c; 或者greenplum这一类的&#xff0c;在企…

Linux 学习路线图

1.应用场景 更加高效地学习并达到运用Linux. 2.学习/操作 linux运维学习需要分为四个阶段&#xff1a;初级入门、中级进阶、高级提升、资深方向细化。 第一阶段&#xff1a;初级入门 初级阶段需要把linux学习路线搞清楚&#xff0c;任何学习都是循序渐进的&#xff0c;所以学…

从零入门机器学习之Linux系统详解

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,科大讯飞比赛第三名,CCF比赛第四名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

为什么要学习Linux?

对于一些偶然接触到Linux的人来说&#xff0c;好奇是对于这个陌生名词的的第一印象。也许这个名字经常出现在你所使用的教科书上&#xff0c;或者是一些技术性的文章上&#xff0c;你却不知其意&#xff0c;此时这个名字再次出现&#xff0c;你就更是好奇了&#xff0c;Linux到…