当前位置: 首页 > news >正文

二叉树遍历(前中后序的递归/非递归遍历、层序遍历)

二叉树的遍历

1. 二叉树的前序、中序、后序遍历

  • 前、中、后序遍历又叫深度优先遍历
    • 注:严格来说,深度优先遍历是先访问当前节点再继续递归访问,因此,只有前序遍历是严格意义上的深度优先遍历
  • 首先需要知道下面几点:
  1. 任何一颗二叉树,都由根节点、左子树、右子树构成。

​ 如图:

  1. 分治算法:分而治之。大问题分成类似的子问题,子问题再分成子问题……直到子问题不能再分割。对树也可以做类似的处理,对一棵树不断地分割,直到子树为空时,分割停止。
  2. 关于二叉树的许多问题我们都要利用分治思想,将一棵完整的树分解成根和左右两棵子树,然后再对这两棵子树进行相同的递归处理,最后得到结果。
  3. 如果对递归的过程想的不太清楚,建议画递归展开图来辅助理解

1.1 前序(先根)遍历

  • 遍历顺序:根- > 左子树 -> 右子树(即先访问根,再访问左子树,最后在访问右子树)

  • 如上图中:A -> B -> C -> NULL -> NULL -> D -> NULL -> NULL -> E -> NULL -> NULL

typedef char BTDataType;
typedef struct BinaryTreeNode
{struct BinaryTreeNode *left;	//指向左子树struct BinaryTreeNode *right;	//指向右子树BTDataType data;
}BTNode;void PrevOrder(BTNode * root)	//前序遍历
{if (!root)return;printf("%d ",root->data);	//根PrevOrder(root->left);		//左子树PrevOrder(root->right);		//右子树return;
}

递归顺序图:

在这里插入图片描述

递归展开图:

1.2 中序(中根)遍历

  • 遍历顺序:左子树 -> 根 -> 右子树(即先访问左子树,再访问根,最后在访问右子树)

  • 如上图中:NULL -> C -> NULL -> B -> NULL -> D -> NULL -> A -> NULL -> E -> NULL

void InOrder(BTNode* root)
{if (!root)return;InOrder(root->left);	//左子树printf("%c ", root->data);		//根InOrder(root->right);	//右子树return;
}

递归顺序图:

在这里插入图片描述

1.3 后序(后根)遍历

  • 遍历顺序:左子树 -> 右子树 -> 根(即先访问左子树,再访问左=右子树,最后在访问根)

  • 如上图中:NULL -> NULL -> C -> NULL -> NULL -> D -> B -> NULL -> NULL -> E -> A

void PostOrder(BTNode* root)
{if (!root){printf("NULL ");return;}PostOrder(root->left);	//左子树PostOrder(root->right);		//右子树printf("%c ", root->data);		//根
}

递归顺序图:

在这里插入图片描述

2. 二叉树前中后序的非递归遍历

  • 在利用递归来进行二叉树的前中后序遍历时,我们通常将一棵二叉树看成三部分:根、左子树、右子树
  • 但是对于前中后序的非递归遍历,我们需要转变思路:应当将一棵二叉树看成两部分:左路节点、左路节点的右子树

在这里插入图片描述

2.1 前序非递归

前序遍历的顺序是:根 -> 左子树 -> 右子树

具体到一棵二叉树,就是自顶向下将左路节点遍历完,再自底向上遍历左路节点的右子树。如图:

在这里插入图片描述

为了能够在遍历完左路节点后还能得到这个节点从而得到这个节点的右子树,我们需要利用栈来对左路节点进行存储

实现代码:

class Solution {
public:vector<int> preorderTraversal(TreeNode* root) {vector<int> ret;stack<TreeNode*> st;TreeNode* cur = root;while (cur || !st.empty()){//遍历左路节点,将左路节点的值打印的同时将节点入栈while (cur){ret.push_back(cur->val);st.push(cur);cur = cur->left;}TreeNode* tmp = st.top();st.pop();//此时cur即为左路节点的右子树//将这棵右子树看成一颗完整的二叉树,进行相同的操作cur = tmp->right;}return ret;}
};

2.2 中序非递归

中序遍历的顺序是:左子树 -> 根 -> 右子树

具体到一棵二叉树,即从左路节点的最深处开始,先遍历这个节点,再遍历这个节点的右子树,自底向上。

在这里插入图片描述

同样,为了能够找到之前的左路节点,也需要一个栈来对左路节点进行保存

实现代码:

class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> ret;stack<TreeNode*> st;TreeNode* cur = root;while (cur || !st.empty()){//遍历左路节点的时候将左路节点入栈//由于左子树先于根,因此先不要打印左路节点(根)的值while (cur){st.push(cur);cur = cur->left;}//程序第一次走到这里时,tmp就是左路节点的最深处//tmp的左子树为nullptr(或已经遍历完tmp的左子树),因此打印其(根)值TreeNode* tmp = st.top();st.pop();ret.push_back(tmp->val);//遍历左路节点的右子树cur = tmp->right;}return ret;}
};

2.3 后序非递归

后序的遍历顺序为:左子树 -> 右子树 -> 根

具体到一棵二叉树,即从左路节点的最深处开始,先遍历左路节点的右子树,再遍历左路节点,自底向上。如图:

在这里插入图片描述

同样,也需要一个栈来保存之前的左路节点

此外,由于后序遍历的顺序为:左子树 -> 右子树 -> 根,需要遍历完根(左路节点)的左子树和右子树后才能对其值进行打印,在这个过程中,我们会经过两次根,且只能在第二次经过根时才能打印根的值,为了确保我们打印根的时机,可以利用一个指针prev来记录之前遍历的位置如果prev停留在左路节点的右子树的根节点,就说明此时左右子树已经遍历完,可以打印根的值

实现代码:

class Solution {
public:vector<int> postorderTraversal(TreeNode* root) {vector<int> ret;stack<TreeNode*> st;TreeNode* cur = root;TreeNode* prev = nullptr;while (cur || !st.empty()){//将左路节点入栈while (cur){st.push(cur);cur = cur->left;}TreeNode* tmp = st.top();//如果左路节点的右子树为空或者prev停留在右子树的根//说明根的左子树和右子树都已遍历完//打印根值(遍历根),同时跟新prev的位置if (tmp->right == nullptr || prev == tmp->right){ret.push_back(tmp->val);st.pop();prev = tmp;}else	//否则,说明根的右子树没有遍历完,遍历右子树cur = tmp->right;}return ret;}
};

2. 二叉树的层序遍历

  • 层序遍历又叫广度优先遍历

  • 设二叉树的根节点所在层数为1,层序遍历就是从根节点出发,首先访问第一层的节点,然后从左到右访问第二层上的节点,接着访问第三层的节点,以此类推,自上而下,自左往右逐层访问树的结点的过程就是层序遍历

  • 层序遍历借助队列的先进先出思想来实现

  • 核心思想:上一层带下一层

  • 如图就是对上面那棵树的层序遍历示意图:

    在这里插入图片描述

  • 实现代码

typedef BTNode* QDataType;		//队列元素类型
typedef struct QueueNode
{struct QueueNode* next;QDataType data;
}QueueNode;
typedef struct Queue	//定义存放指向队头,队尾指针的结构体
{QueueNode* head;	//指向队头QueueNode* tail;	//指向队尾
}Queue;//层序遍历
void LevelOrder(BTNode* root)		
{Queue *q = (Queue *)malloc(sizeof(Queue));	//创建队列QueueInit(q);	//初始化队列//如果根节点为空,直接退出函数if (!root)return;QueuePush(q, root);		//先将根节点入队入队while (!QueueEmpty(q))		//当队列不为空{BTNode* front = QueueFront(q);		//接收队头元素QueuePop(q);		//出队头元素printf("%c ", front->data);	//访问该节点if (front->left)		//如果左子树不为空QueuePush(q, front->left);			//左子树入队if (front->right)		//如果右子树不为空QueuePush(q, front->right);		//右子树入队}printf("\n");QueueDestroy(q);		//销毁队列	
}

3. 二叉树遍历的应用

在这里插入图片描述

  • 由二叉树和层序遍历的思想,我们可以构造出这棵树

  • 再有前序遍历 根- > 左子树 -> 右子树 的思想,可以知道,这棵树的前序序列为:A B D H E C F G

在这里插入图片描述

  • 这道题是由二叉树的前序序列和中序序列来确定二叉树,我们知道中序遍历的思想是 左子树 -> 根 -> 右子树 ,根将左子树和右子树分割开来,那么我们就可以先用前序序列确定根,再用中序序列确定根的左右子树,这样就可以将这棵二叉树确定了,如图:

在这里插入图片描述

  • 显然根节点为E

我们同样可以用代码,利用一棵二叉树的前序序列和中序序列来将这棵二叉树还原👉Leetcode -> 从前序与中序遍历序列构造二叉树

class Solution {
public://前序序列即为根序列//在中序序列找到根后可以将序列分为左右两部分,这两部分分别就是跟的左子树序列和右子树序列TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int inBegin, int inEnd){//区间长度小于1,直接返回空if (inBegin > inEnd)return nullptr;//前序序列即为根序列TreeNode* root = new TreeNode(preorder[prei++]);//在中序序列中找到根int pos = 0;while (1){if (root->val != inorder[pos])pos++;elsebreak;}//分别前往左子树和右子树进行连接root->left = _buildTree(preorder, inorder, prei, inBegin, pos - 1);root->right = _buildTree(preorder, inorder, prei, pos + 1, inEnd);return root;}TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {int i = 0;TreeNode* root = _buildTree(preorder, inorder, i, 0, inorder.size() - 1);return root;}
};

在这里插入图片描述

  • 这题和第二题类似,同样是先由后序遍历(左子树 -> 右子树 -> 根)确定根节点,再由中序遍历确定根的左右子树,只是用后序遍历确定根节点时要从最后开始。如图:

在这里插入图片描述

  • 易得前序遍历为a b c d e

  • 我们同样可以用代码,利用一棵二叉树的前序序列和中序序列来将这棵二叉树还原👉Leetcode -> 从中序与后序遍历序列构造二叉树

class Solution {
public://思想同前序序列和中序序列确定二叉树类似TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int& posi, int inBegin, int inEnd){if (inBegin > inEnd)return nullptr;TreeNode* root = new TreeNode(postorder[posi--]);int pos = 0;while (1){if (root->val != inorder[pos])pos++;elsebreak;}root->right = _buildTree(inorder, postorder, posi, pos + 1, inEnd);root->left = _buildTree(inorder, postorder, posi, inBegin, pos - 1);return root;}TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {int i = postorder.size() - 1;TreeNode* root = _buildTree(inorder, postorder, i, 0, inorder.size() - 1);return root;}
};

总结:

由于二叉树的中序遍历可以分割二叉树的左右节点,因此 前序序列 + 中序序列 / 后序序列 + 中序序列 都可以构建出一棵二叉树,而单独序列和 前序序列 + 后序序列就不行。

相关文章:

二叉树遍历(前中后序的递归/非递归遍历、层序遍历)

二叉树的遍历 1. 二叉树的前序、中序、后序遍历 前、中、后序遍历又叫深度优先遍历 注&#xff1a;严格来说&#xff0c;深度优先遍历是先访问当前节点再继续递归访问&#xff0c;因此&#xff0c;只有前序遍历是严格意义上的深度优先遍历 首先需要知道下面几点&#xff1a; …...

UE4升级UE5 蓝图节点变更汇总(4.26/27-5.2/5.3)

一、删除部分 Ploygon Editing删除 Polygon Editing这个在4.26、4.27中的插件&#xff0c;在5.1后彻底失效。 相关的蓝图&#xff0c;如编辑器蓝图 Generate mapping UVs等&#xff0c;均失效。 如需相关功能&#xff0c;请改成Dynamic Mesh下的方法。 GetSupportedClass删…...

【python】异常处理

前言 省略各种废话&#xff0c;直接快速整理知识点 try-except 基础 作用 程序不可能永远都是对的&#xff0c;当7除a&#xff0c;a由用户输入时&#xff0c;用户输入0就会报错。try-except就是解决这些问题。 结构 多分支自定义错误类型 上方的exception是一个错误类型…...

【xv6操作系统】Lab systems calls

一、实验前须知 阅读 xv6 文档的第 2 章和第 4 章的 4.3 节和 4.4 节以及相关源文件&#xff1a; 系统调用的用户空间代码在 user/user.h 和 user/usys.pl 中。 内核空间代码在 kernel/syscall.h 和 kernel/syscall.c 中。 与进程相关的代码在 kernel/proc.h 和 kernel/proc.c…...

python的scripts文件夹作用

Windows系统&#xff1a; Scripts文件夹通常位于Python的安装目录下&#xff0c;如C:\Python\Scripts。该文件夹内包含了各种有用的工具&#xff0c;例如pip、virtualenv等&#xff0c;这些工具有助于管理和配置Python环境和依赖包。 Linux系统&#xff1a; 在Linux系统中&…...

Discuz论坛网站报错Discuz!Database Error(0)notconnect的解决办法

运营服务器大本营有段时间了&#xff0c;在运营期间遇到两次Discuz&#xff01;Database Error&#xff08;0&#xff09;notconnect报错&#xff0c;和你们分享遇到Discuz报错的解决办法&#xff0c;希望可以帮助到你。 首先网站报错&#xff08;0&#xff09;notconnect&…...

掌握mysql,看完这篇文章就够了

​数据库 对大量数据进行存储和管理&#xff08;增删改查&#xff09; 客户端&#xff1a; 黑窗口终端navicat 熊掌软件数据库分类&#xff1a; 关系型数据库 通过表与表产生关联关系&#xff0c;每个表中都存储结构化数据&#xff0c;支持sql结构化查询语言MysqlOracleSQLS…...

守护Web安全:了解Web攻击与防护策略

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

变换,动画

面试题——需求&#xff1a;在不知道父元素与子元素的宽高时 如何让子元素在父元素内居中&#xff1f; 1.定位 父相子绝 2.子元素 top&#xff1a;50% left:50% 3.子元素 transform: translate(-50%,-50%) .parent{height: 500px;background-color: red;position: relative;}.c…...

深度解析速卖通商品详情API:Python实战与高级技术探讨

速卖通商品详情API接口实战&#xff1a;Python代码示例 一、准备工作 在开始之前&#xff0c;请确保你已经完成了以下步骤&#xff1a; 在速卖通开放平台注册账号并创建应用&#xff0c;获取API密钥。阅读速卖通商品详情API接口的文档&#xff0c;了解接口的使用方法和参数要…...

背包问题算法

背包问题算法 0-1背包问题二维数组一维数组 完全背包问题二维数组一维数组 多重背包问题一维数组 0-1背包问题 问题&#xff1a;背包的容量为9&#xff0c;有重量分别为[2, 4, 6, 9]的四个物品&#xff0c;价值分别为[3, 4, 5, 6]&#xff0c;求背包能装的物品的最大价值是多少…...

echarts柱状图可鼠标左击出现自定义弹框,右击隐藏弹框并阻止默认右击事件

每项x轴数据对应有两条柱图和一条阴影效果是学习其它博客得到的效果&#xff0c;这个是学习的原文链接&#xff1a;echarts两个合并柱体&#xff08;普通柱状图象形柱图&#xff09;共享一个柱体阴影 因为这次情况比较特殊&#xff0c;不仅需要自定义弹框内容&#xff0c;而且…...

存算一体成为突破算力瓶颈的关键技术?

大模型的训练和推理需要高性能的算力支持。以ChatGPT为例&#xff0c;据估算&#xff0c;在训练方面&#xff0c;1746亿参数的GPT-3模型大约需要375-625台8卡DGX A100服务器训练10天左右&#xff0c;对应A100 GPU数量约3000-5000张。 在推理方面&#xff0c;如果以A100 GPU单卡…...

Pytorch_1_基本语法

一、Pytorch的基本元素操作 1.引入torch from __future__ import print_function import torch 2.创建矩阵 x torch.empty(5,3) print(x) 3.输出结果&#xff1a; tensor([[7.9191e34, 1.1259e24, 1.2359e-42], [4.0824e-40, 1.1379e-35, 2.5353e30], [8.…...

2024上海国际玻璃纤维及新材料展览会

2024上海国际玻璃纤维及新材料展览会 时间&#xff1a;2024年12月18&#xff5e;20日 地点&#xff1a;上海新国际博览中心 ◆ 》》》展会概况&#xff1a; 玻璃纤维是一种性能优异的无机非金属材料&#xff0c;比有机纤维耐温高&#xff0c;不燃&#xff0c;抗腐&#xff…...

云计算项目九:K8S安装

K8S安装 Kube-master安装 按照如下配置准备云主机 防火墙相关配置&#xff1a;禁用selinux&#xff0c;禁用swap&#xff0c;且在firewalld-*。上传kubernetes.zip 到跳板机 配置yum仓库&#xff08;跳板机&#xff09; 跳板机主机配置k8s软件源服务端 [rootjs ~]# yum -y…...

sign加密方法生成

1. 引入包的问题 2. 原因 .pycrypto、pycrytodome和crypto是一个东西&#xff0c;crypto在python上面的名字是pycrypto&#xff0c;它是一个第三方库&#xff0c;但是已经停止更新 3. 解决方法 --直接安装&#xff1a;pip install pycryptodome 3.但是&#xff0c;在使用的时…...

【Linux】编译器-gcc/g++使用

个人主页 &#xff1a; zxctscl 文章封面来自&#xff1a;艺术家–贤海林 如有转载请先通知 文章目录 1. 前言2. 初见gcc和g3. 程序的翻译过程3.1 预处理3.1.1 宏替换 去注释 头文件展开3.1.2 条件编译 3.2 编译3.3 汇编3.4 链接 4. 链接4.1 动态链接4.2 静态链接 1. 前言 在之…...

Python 中的 filter() 函数:筛选可迭代对象元素

在 Python 中&#xff0c;filter() 函数是一个非常有用的内置函数&#xff0c;用于根据指定条件过滤可迭代对象中的元素。本文将深入探讨 filter() 函数的用法、工作原理以及常见应用场景&#xff0c;以帮助大家更好地理解和运用这个函数。 什么是 filter() 函数&#xff1f; …...

Java高频面试之并发篇

有需要互关的小伙伴,关注一下,有关必回关,争取今年认证早日拿到博客专家 并行和并发有什么区别&#xff1f; 并行是同时执行多个任务&#xff0c;而并发是多个任务在一段时间内交替执行。并行&#xff08;Parallel&#xff09;是指同时执行多个任务或操作&#xff0c;通过同时…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...