二叉树的前序、中序、后序、层序遍历

news2024/11/22 19:08:56

参考内容:

五分钟让你彻底理解二叉树的非递归遍历

Python实现二叉树的非递归遍历

二叉树遍历——深度优先(前中后序)+广度优先(层序遍历)

构造二叉树

定义二叉树结构如下

struct node
{
    int data;
    node *left;
    node *right;
};

构造如下形态二叉树

node *init_tree()
{
    node *node1 = (node *)malloc(sizeof(node));
    node *node2 = (node *)malloc(sizeof(node));
    node *node3 = (node *)malloc(sizeof(node));
    node *node4 = (node *)malloc(sizeof(node));
    node *node5 = (node *)malloc(sizeof(node));
    node *node6 = (node *)malloc(sizeof(node));
    node *node7 = (node *)malloc(sizeof(node));
    node *node8 = (node *)malloc(sizeof(node));
    
    node1->data = 1;
    node2->data = 2;
    node3->data = 3;
    node4->data = 4;
    node5->data = 5;
    node6->data = 6;
    node7->data = 7;
    node8->data = 8;

    node1->left = node2;
    node1->right = node3;
    
    node2->left = node4;
    node2->right = node5;
    
    node3->right = node6;
    
    node5->left = node7;
    node5->right= node8;

    return node1;
}

前序遍历(递归)

前序遍历顺序为根左右。要遍历整个二叉树我们就需要遍历二叉树的每一个子树,对于任何一个子树它的遍历方式均为根左右顺序遍历。即所有子问题均与父问题除规模大小不同外,其余均相同。所以可以采用递归方式实现前序遍历。

// 前序遍历 根左右
void pre_order_traversal(node *root)
{
    if(root) {
        cout<<root->data<<" ";
        pre_order_traversal(root->left);
        pre_order_traversal(root->right);
    }
}

遍历结果为:1 2 4 5 7 8 3 6

中序遍历(递归)

中序遍历顺序为左根右。其与前序遍历仅顺序不同,其余均相同。

// 中序遍历 左根右
void in_order_traversal(node *root)
{
    if(root) {
        in_order_traversal(root->left);
        cout<<root->data<<" ";
        in_order_traversal(root->right);
    }
}

遍历结果为:4 2 7 5 8 1 3 6

后序遍历(递归)

后序遍历顺序为左右根。其与前序、中序遍历仅顺序不同,其余均相同。

// 后序遍历 左右根
void post_order_traversal(node *root)
{
    if(root) {
        post_order_traversal(root->left);
        post_order_traversal(root->right);
        cout<<root->data<<" ";
    }
}

遍历结果为:4 7 8 5 2 6 3 1

前序遍历方法一(非递归)

因为递归实际上是由系统帮我们进行压栈,所以理论上所有递归算法都可以改为循环+栈实现,那么我们先照着上述前序遍历的样子修改为循环+栈的形态。需要注意的是由于栈先进后出的特性,为了保证左孩子在右孩子前被访问,所以应该先右孩子入栈,再左孩子入栈。

// 前序遍历 根左右
void pre_order_traversal(node *root)
{
    stack<node *> s;
    s.push(root);

    while(!s.empty()) {

        node *cur = s.top();
        s.pop();

        if(cur) {
            cout<<cur->data<<" ";
            s.push(cur->right);
            s.push(cur->left);
        }
    }
}

遍历结果为:1 2 4 5 7 8 3 6

前序遍历方法二(非递归)

现在我们换一种思路来实现前序非递归遍历,仔细观察前序遍历的递归调用过程。

  1. 先把从根结点开始的所有左子树放入栈中;
  2. 弹出栈顶元素
  3. 如果栈顶元素有右子树,那么右子树入栈
  4. 重复上述过程直到栈为空

因此我们可以写出遍历代码

// 前序遍历 根左右
void pre_order_traversal(node *root)
{
    stack<node *> s;
    node *cur = root;

    while(cur || !s.empty()) {
        // 将左子树全部入栈
        while(cur) {
            cout<<cur->data<<" ";
            s.push(cur);
            cur = cur->left;
        }

        if(!s.empty()) {
            cur = s.top();
            s.pop();
            cur = cur->right;
        }
    }
}

遍历结果为:1 2 4 5 7 8 3 6

中序遍历(非递归)

有了前面的基础,我们再来考虑中序遍历,会发现中序遍历与前序遍历只是打印结点的位置不一样。前序遍历是在结点入栈时打印,中序遍历只需要替换为在结点出栈时打印即可。

// 中序遍历 左根右
void in_order_traversal(node *root)
{
    stack<node *> s;
    node *cur = root;

    while(cur || !s.empty()) {
        // 将左子树全部入栈
        while(cur) {
            s.push(cur);
            cur = cur->left;
        }

        if(!s.empty()) {
            cur = s.top();
            cout<<cur->data<<" ";
            s.pop();
            cur = cur->right;
        }
    }
}

遍历结果为:4 2 7 5 8 1 3 6

后序遍历方法一(非递归)

后序遍历相对来说显得更加复杂了。在前序和中序遍历中,只要左子树处理完毕实际上栈顶元素就可以出栈了,但后序遍历需要把左子树和右子树都处理完毕才能出栈,显然我们需要某种方法记录遍历的过程。

实际上我们只需要记录下遍历的前一个结点就能解决问题,因为通过前一个结点我们可以做如下判断:

  1. 如果前一个结点是当前结点的右子树,那么说明右子树已经遍历完毕可以出栈了
  2. 如果前一个结点是当前结点的左子树而且当前结点没有右子树,那么说明可以出栈了
  3. 如果当前结点即没有左子树也没有右子树,即为叶子结点,那么说明可以出栈了

若不属于上述情况,则依次将当前结点的右孩子和做孩子入栈,这样就能保证每次取栈顶元素时,左孩子都在右孩子前面被访问,左孩子和右孩子都在父结点前面被访问。

// 后序遍历 左右根
void post_order_traversal(node *root)
{
    stack<node *> s;
    node *pre = NULL;
    node *cur = root;

    s.push(cur);

    while(!s.empty()) {
        cur = s.top();
        // 叶子结点
        if((!cur->left && !cur->right) // 叶子结点
        || pre == cur->right // 前一个结点为当前结点右子树
        || (pre == cur->left && !cur->right)) { // 前一个结点为当前结点左子树,且没有右子树
            cout<<cur->data<<" ";
            pre = cur;
            s.pop();
        } else {
            if(cur->right)
                s.push(cur->right);

            if(cur->left)
                s.push(cur->left);
        }
    }
}

遍历结果为:4 7 8 5 2 6 3 1

后序遍历方法二(非递归)

后序遍历的顺序是左右根,如果把这个顺序倒过来就是根右左,是不是发现和前序遍历很像?那么我只需要按照根右左的方式遍历完,然后将遍历结果掉一个个儿就可以,而栈就具备掉个儿的功能,因此可写出如下代码。

// 后序遍历 左右根
void post_order_traversal(node *root)
{
    stack<node *> s;
    stack<int> ans;
    node *cur = root;

    while(cur || !s.empty()) {
        // 将左子树全部入栈
        while(cur) {
            ans.push(cur->data);
            s.push(cur);
            cur = cur->right;
        }

        if(!s.empty()) {
            cur = s.top();
            s.pop();
            cur = cur->left;
        }
    }

    while(!ans.empty()) {
        cout<<ans.top()<<" ";
        ans.pop();
    }
}

遍历结果为:4 7 8 5 2 6 3 1

层序遍历

层序遍历即广度优先遍历,使用队列即可实现。

// 层序遍历
void breadth_first_order_traversal(node *root)
{
    queue<node *> q;
    q.push(root);
	while(!q.empty()){
		node *cur = q.front();
		q.pop();
		if(cur){
			cout<<cur->data<<" ";
			q.push(cur->left);
			q.push(cur->right);
		}
	}
}

遍历结果为:1 2 3 4 5 6 7 8

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1182254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

移远通信蝉联“年度杰出创新企业”大奖,以核心技术实力永攀行业高峰

11月2日&#xff0c;“国际集成电路展览会暨研讨会”&#xff08;IIC Shenzhen 2023&#xff09;在深圳大中华交易广场重磅启幕。业界领袖共探国内外创新技术与产品成果&#xff0c;并对推动全球电子产业创新做出贡献的企业进行了表彰。其中&#xff0c;全球领先的物联网整体解…

Android 使用.9图 NinePatchDrawable实现动态聊天气泡

最近一段时间&#xff0c;在做一个需求&#xff0c;需要实现一个聊天气泡的动画效果&#xff0c;如下图所示&#xff1a; GitHub源码demo &#xff0c;建议下载demo&#xff0c;运行查看。 动态聊天气泡动画 静态聊天气泡 经过一段时间调研&#xff0c;实现方案如下: 实现方…

使用Redis实现缓存及对应问题解决

一、为什么需要Redis作缓存&#xff1f; 在业务场景中&#xff0c;如果有些数据需要极高频的存取&#xff0c;每次都要在mysql中查询的话代价太大&#xff0c;假如有一个存在于客户端和mysql之间的存储空间&#xff0c;每次可以在这空间中进行存取操作&#xff0c;就会减轻mys…

Docker配置Nginx反向代理

文章目录 1.部署微程序到docker中1.1 dockerfile文件1.2 依据自定义的dockerfile文件创建docker镜像1.3 创建容器1.4 测试 2.在docker中安装Nginx2.1 安装Nginx镜像2.2 获取Nginx配置文件并将其同步到宿主电脑指定位置中安装nginx容器删除nginx容器 2.3 安装Nginx容器并数据挂载…

C++: 类和对象(中) (构造函数, 析构函数, 拷贝构造函数, 赋值重载, 取地址重载)

文章目录 1. 类的6个默认成员函数2. 构造函数构造函数概念构造函数特性特性1,2,3,4特性5特性6特性7 3. 析构函数析构函数概念析构函数特性特性1,2,3,4特性5特性6 4. 拷贝构造函数拷贝构造函数概念拷贝构造函数特性特性1,2特性3特性4特性5 5. 运算符重载一般运算符重载赋值运算符…

mysql安装成功

先在官网下载 地址&#xff1a;MySQL :: Download MySQL Community Server下载的 下载的这个 解压后 zip格式是自己解压&#xff0c;解压缩之后其实MySQL就可以使用了&#xff0c;但是要进行环境变量配置 我的电脑->属性->高级->环境变量->系统变量 选择Path,在其…

java版直播商城免费搭建平台规划及常见的营销模式+电商源码+小程序+三级分销+二次开发

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

二十、泛型(3)

本章概要 构建复杂模型泛型擦除 C 的方式迁移兼容性擦除的问题边界处的动作 构建复杂模型 泛型的一个重要好处是能够简单安全地创建复杂模型。例如&#xff0c;我们可以轻松地创建一个元组列表&#xff1a; TupleList.java import java.util.ArrayList;public class TupleL…

深入理解强化学习——多臂赌博机:乐观初始值

分类目录&#xff1a;《深入理解强化学习》总目录 目前为止我们讨论的所有方法都在一定程度上依赖于初始动作值 Q 1 ( a ) Q_1(a) Q1​(a)的选择。从统计学角度来说&#xff0c;这些方法&#xff08;由于初始估计值&#xff09;是有偏的。对于采样平均法来说&#xff0c;当所有…

软件专业毕业生的如何找工作?——加速度jsudo

据统计&#xff0c;2023届全国高校毕业生预计达到1158万人&#xff0c;同比增长82万人。根据某大学的统计数据&#xff0c;IT专业的就业率在过去五年中保持了稳定增长的趋势&#xff0c;平均超过90%。 IT行业的薪资水平相对较高&#xff0c;也让很多高校和培训机构愿意投入更多…

Sybase连接详解

Sybase连接详解 Sybase连接详解摘要一、JDBC基础1.1 JDBC简介1.2 JDBC驱动程序 二、配置Sybase JDBC连接2.1 连接Sybase数据库2.2 验证Sybase JDBC连接2.3 获取Sybase数据库表信息和注释2.4 根据表名获取Sybase字段信息和注释2.5 执行SQL查询2.6 插入数据2.7 执行Sybase存储过程…

ElasticSearch离线安装

1. 上传和解压软件 将elasticsearch-7.11.2-linux-x86_64.tar.gz和kibana-7.11.2-linux-x86_64.tar.gz 上传到/data/es目录 解压文件 tar -zxvf elasticsearch-7.11.2-linux-x86_64.tar.gz tar -zxvf kibana-7.11.2-linux-x86_64.tar.gz 2. 创建es用户 因为安全问题&#xff…

windows好玩的cmd命令

颜色 后边的数字查表吧,反正我是喜欢一个随机的数字 color 01MAC getmac /v更新主机IP地址 通过DHCP更新 ipconfig /release ipconfig /renew改标题 title code with 你想要的标题

如何实现Word文档中的书签双向定位

工作中&#xff0c;经常需要拟定合同&#xff0c;一般都有固定的模板&#xff0c;在特定的位置填写内容。通过zOffice编辑合同文件时&#xff0c;可以在模板需要填写的位置预设书签&#xff0c;配合zOffice SDK使用&#xff0c;利用zOffice书签双向定位的特性&#xff0c;更方便…

3.5、Linux:命令行git的使用

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 在Linux Centos7.6下安装git yum -y install git 注册一个gitee账号 进去注册就好&#xff0c;记住自己的用户名和密码。 创建一个仓库 点击复制&#xff0c;接着就可以在Linux上使用了 git clone git clone 刚才复制的地…

《UML和模式应用(原书第3版)》2024新修订译本部分截图

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 机械工业出版社即将在2024春节前后推出《UML和模式应用&#xff08;原书第3版&#xff09;》的典藏版。 受出版社委托&#xff0c;UMLChina审校了原中译本并做了一些修订。同比来说&a…

Qwt QwtThermo绘制温度计

1.简介 QwtThermo 是一个基于 Qt 框架的类库&#xff0c;用于创建温度计控件。它提供了一些方便的功能来展示和处理温度计相关的数据。 QwtThermo 添加了特定于温度计的功能。 使用 QwtThermo&#xff0c;可以实现以下功能&#xff1a; 设置温度范围&#xff1a;可以通过设置…

golang正则获取中括号中的内容

reg : regexp.MustCompile("【(.*?)】") //userInfo姓名:【AAA姓名】证件类型:【BBB身份证】证件号码:【122456789458】tempData reg.FindAllStringSubmatch(userInfo, -1)for k, v : range tempData {if k 0 {tempReleaseUser.Name v[1]//AAA姓名} else if k 1…

选择企业云盘?品牌推荐和评价解析

企业云盘是如今热门的企业协作工具&#xff0c;为企业提供了文件存储、文件共享服务。市面上的企业云盘千千万&#xff0c;到底哪个企业云盘好用&#xff1f;哪些品牌值得信赖呢&#xff1f; 好用的企业云盘&#xff0c;不能不提&#xff0c;Zoho Workdrive企业云盘为企业提供…

养老院展示服务预约小程序的作用是什么

养老院无论在哪个城市都有很高需求度&#xff0c;不少银发人群会因为种种原因而前往&#xff0c;而养老院近些年来各种服务也比较完善&#xff0c;增加了客户信任度及接受度&#xff0c;但对院方来说&#xff0c;也存在着一些痛点&#xff1a; 1、品牌传播服务呈现难 养老院也…