数据结构【二叉树的遍历实现】

📘考研数据结构基础:二叉树的存储、遍历与队列辅助实现详

在这里插入图片描述

在数据结构的学习中,二叉树作为一种结构清晰、应用广泛的树形结构,是考研计算机专业课中重点内容之一。本文将以实际代码为基础,介绍二叉树的存储结构遍历方式,以及在遍历过程中为何要使用队列结构,并解答一个常见疑问:“为什么不能用 char 类型直接代替队列的元素类型”。

🧩一、二叉树的存储方式:链式存储结构

在实际开发或算法设计中,二叉树常采用链式存储结构。其基本定义如下:

typedef struct BiTNode {char data;struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;
  • 每个结点由一个字符 data 表示数据域。
  • lchildrchild 分别指向该结点的左、右子树。
  • BiTree 是指向 BiTNode 的指针,代表一个树的根结点。

🧪二、二叉树的遍历方式

✅ 1. 先序遍历(PreOrder)

void PreOrder(BiTree T) {if (T) {cout << T->data;PreOrder(T->lchild);PreOrder(T->rchild);}
}

先访问根结点,再递归遍历左子树和右子树。


✅ 2. 中序遍历(InOrder)

void InOrder(BiTree T) {if (T) {InOrder(T->lchild);cout << T->data;InOrder(T->rchild);}
}

先访问左子树,再访问根结点,最后遍历右子树。


✅ 3. 后序遍历(PostOrder)

void PostOrder(BiTree T) {if (T) {PostOrder(T->lchild);PostOrder(T->rchild);cout << T->data;}
}

先遍历左右子树,最后访问根结点。


✅ 4. 层序遍历(LevelOrder)

层序遍历不同于上面三种递归遍历,它需要用辅助队列实现。

void LevelOrder(BiTree T) {LinkQueue Q;InitQ(Q);EntQ(Q, T);while (!EmptyQ(Q)) {BiTree p;DelQ(Q, p);cout << p->data;if (p->lchild)EntQ(Q, p->lchild);if (p->rchild)EntQ(Q, p->rchild);}
}

🚩三、辅助队列结构及其核心设计

为了支持层序遍历的广度优先访问,我们需要一个队列,队列中存放的是树结点的指针而非字符:

typedef BiTree ElemType;  // 关键设计:定义队列的元素类型为 BiTree(即指向树结点的指针)

队列的基本操作如下:

bool EntQ(LinkQueue &Q, ElemType x);    // 入队
bool DelQ(LinkQueue &Q, ElemType &x);   // 出队

❓为什么不能直接将 ElemType 改为 char?

这是许多初学者常见的疑惑。解释如下:

  • char 仅能存储字符,比如 'A',却无法提供关于该字符结点的结构信息。

  • 层序遍历时,我们需要访问一个结点的左右孩子:

    if (p->lchild) EntQ(Q, p->lchild);
    

    这里 p 是一个指向结构体的指针,如果你仅保存了 char,根本无法访问 p->lchild

✅ 所以,队列中必须保存的是 BiTree 类型的指针,从而能继续递归或迭代处理其子树。


🧵四、二叉树的构建

构建过程通常采用先序递归建树法,使用 '#' 表示空结点:

void CreateTree(BiTree &T) {char ch;cin >> ch;if (ch == '#')T = NULL;else {T = new BiTNode;T->data = ch;CreateTree(T->lchild);CreateTree(T->rchild);}
}

输入示例(先序):

AB#D##C##

表示结构如下的树:

    A/ \B   C\D

🔚五、总结与考研建议

知识点内容
存储结构常用链式存储,结构清晰,动态性强
遍历方法先序、中序、后序、层序,掌握递归与非递归实现
辅助结构层序遍历需要队列,存储的是结点指针 BiTree
设计技巧使用 typedef ElemType 抽象数据类型,增强复用性

完整代码《仅供参考》

function.h

 //层次建树   借助一个辅助队列//          a//        b   c//    d   e   f   g
    #ifndef LEVELIRDER3_FUNCTION_H#define LEVELIRDER3_FUNCTION_H#include <stdio.h>#include <stdlib.h>#include <ostream>using namespace std;//树的结构体定义typedef struct BiTNode{char data;struct BiTNode *lchild,*rchild;}BiTNode , * BiTree;//队列的结构体的声明typedef BiTree ElemType;typedef struct LinkNode{ElemType datac;//在进行入队的时候 这里使用的是 int 类型的的数据 然而树存储的是一个char  类型的这时候就体现出的了 typedef 的作用了 不用频繁的修改数据struct LinkNode * next;}LinkNode;typedef struct {  //声明一个结构体类型的指针LinkNode * front, * rear;}LinkQueue;void InitQ(LinkQueue &Q);bool EmptyQ(LinkQueue Q);//这里出入队列 实际上是对 树 的结点进行 的一个操作 所以应该使用 树的 结构体类型bool EntQ(LinkQueue &Q,ElemType x);bool DelQ(LinkQueue &Q, ElemType &x);// 建立一个辅助队列 tag  进行层序建树typedef struct tag{//    BiTree p;  //树的某一结点的地址值BiTNode *p;struct tag *pnext;}tag_t , *ptag_t;#endif //LEVELIRDER3_FUNCTION_H

queue.cpp

   //// Created by Yhame on 2025/5/11.//#include "function.h"//初始化void InitQ(LinkQueue &Q){//这里的结构体类型  LinkNodeQ.front = Q.rear =(LinkNode*) malloc(sizeof(LinkNode));Q.front->next =NULL; //队头指向NULL}//判空 这里可以不用判断队列是否会满bool EmptyQ(LinkQueue Q){if(Q.front == Q.rear)return true;//    Q.front->next =NULL;//    return true;}//入队bool EntQ(LinkQueue &Q,ElemType x){//插入LinkNode *s =(LinkNode*)malloc(sizeof(LinkNode)); //申请新的结点 插入队尾if(!s)return false;s->datac =x;   //尾巴插入Q.rear->next =s;   //尾部入队  尾指针的指向ss->next =NULL;  //s的指向NULLQ.rear =s;  //更新尾指针return true;}//出队bool DelQ(LinkQueue &Q,ElemType &x){ //判空 从头开始删除if(Q.front ==Q.rear){return false;}LinkNode *q ;x = Q.front->next->datac; //返回要删除的数据q = Q.front->next;  //q指向第一个数据结点  队头出Q.front->next =q ->next;  //断开q 使front 指向后一个元素if(Q.rear==q){Q.rear = Q.front; // rear也回到front(空队列状态)}free(q);//如果q 为最后一个结点  使front 和rear 相等return true;}

main.cpp

     #include <iostream>#include "function.h"//前序遍历void PreOrder(BiTree p){if(p!=NULL){printf("%c",p->data);PreOrder(p->lchild);PreOrder(p->rchild);}}//中序遍历void InOrder(BiTree p){if(p!=NULL){InOrder(p->lchild);printf("%c",p->data);InOrder(p->rchild);}}//后序遍历void PostOrder(BiTree p){if(p!=NULL){PostOrder(p->lchild);PostOrder(p->rchild);printf("%c",p->data);}}//层次遍历void LevelOrder(BiTree T){LinkQueue Q; //声明一个 辅助队列InitQ(Q);BiTree p;  //记录树的当前结点//    BiTNode * p;EmptyQ(Q);EntQ(Q,T);  //树根入队当队列不为空进行 队头出队打印   之后判断 该点的 左右孩子是否为空  进行出入队操作puts("层序");while(!EmptyQ(Q)){DelQ(Q,p); //出队当前结点 并打印putchar(p->data);  //printf("%c,c);if(p->lchild!=NULL){EntQ(Q,p->lchild);}if(p->rchild!=NULL){EntQ(Q,p->rchild);}}}int main() {BiTree pnew; //用来指向新申请的数结点BiTree tree =NULL; //tree 是指向树根的,代表树ptag_t phead= NULL,ptail =NULL, listpnew =NULL, pre =NULL; //初始化队列 定义一个pre 指向执行的当前元素char c;这里使用的是 tag 的方法去建立一棵树,通过辅助队列来实现的 层序遍历//abcdefwhile(scanf("%c",&c)){if(c=='\n'){break;}//calloc申请空间  大小是两个参数相乘,并对空间进行初始化  赋值为0;//malloc 申请以后还需要对其进行赋值 malloc 返回的是 void * 类型的 也需要进行强制转换树申请结点pnew =(BiTree) calloc(1,sizeof(BiTNode));pnew->data = c;//队列结点申请空间listpnew = (ptag_t) calloc(1,sizeof(tag_t));  //申请一个结构体类型的结点 返回一个指针类型的listpnew->p =pnew;//如果是数的第一个结点if(tree==NULL){tree = pnew;  //tree 指向根的头结点//第一个结点 即是队列头也是 队列尾phead = ptail = listpnew;pre = listpnew; //  用来判断当前结点 的左右孩子是否满了}else {//元素直接入队ptail ->pnext = listpnew;ptail =listpnew;//接下来把元素放入树中if(pre->p->lchild ==NULL){pre ->p ->lchild =pnew;  // pre -> p 左孩子为空 就放入左孩子}else if(pre->p->rchild==NULL){pre->p->rchild =pnew;pre = pre->pnext;  // !!! 左右孩子都满了,指向下一个节点}}}puts("前序");PreOrder(tree);printf("\n");puts("中序");InOrder(tree);printf("\n");puts("后序");PostOrder(tree);printf("\n");LevelOrder(tree);return 0;//这没有对树进行相应的输出  ,使用调试发现建树完成}//abcdefg//        前序//abdecfg//        中序//dbeafcg//        后序//debfgca//        层序//abcdefg

这个代码实现了通过层次输入字符数据来构建一棵二叉树,并对这棵二叉树进行前序、中序、后序、层序遍历,完整地展示了树的构建与遍历过程。


✅ 具体功能如下:

1. 层次建树(用辅助队列实现)

  • 利用自定义的tag_t 辅助队列结构体进行层次建树。
  • 每次输入一个字符就创建一个二叉树结点。
  • 如果当前树为(即首次输入),设置为根节点;
  • 后续的结点依次作为上一结点的左孩子右孩子插入;
  • 每插入完一个结点,就将其作为队列中的一个元素排队,继续等待为它的孩子分配子节点。

📘写在最后

学习数据结构尤其是二叉树,最重要的是掌握“结构 + 操作”的关系。每一次遍历、每一次入队出队,背后都是指针、结构体、递归这些基础能力的体现。希望本文结合实际代码的讲解能为你的学习提供实用帮助。

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

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

相关文章

无人机俯视风光摄影Lr调色预设,手机滤镜PS+Lightroom预设下载!

调色详情 无人机俯视风光摄影 Lr 调色是利用 Adobe Lightroom 软件&#xff0c;对无人机从俯视角度拍摄的风光照片进行后期处理的调色方式。通过调整色彩、对比度、光影等多种参数&#xff0c;能够充分挖掘并强化画面独特视角下的壮美与细节之美&#xff0c;让原本平凡的航拍风…

【springcloud学习(dalston.sr1)】Eureka服务端集群的搭建(含源代码)(二)

该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍&#xff08;含源代码&#xff09;&#xff08;一&#xff09; 这篇文章主要介绍多个eureka服务端的集群环境是如何搭建的。 &#xff08;一&#xff09;eureka的简要说明 Eu…

互联网大厂Java求职面试实战:Spring Boot微服务与数据库优化详解

&#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&#xff0c;毕业季咱们不慌忙&#xff0c;几百款毕业设计等你选。 ❤️ 3. Python爬虫专栏…

事件驱动reactor的原理与实现

fdset 集合&#xff1a;&#xff08;就是说&#xff09; fd_set是一个位图&#xff08;bitmap&#xff09;结构 每个位代表一个文件描述符 0表示不在集合中&#xff0c;1表示在集合中 fd_set结构&#xff08;简化&#xff09;&#xff1a; [0][1][2][3][4][5]...[1023] …

一分钟在Cherry Studio和VSCode集成火山引擎veimagex-mcp

MCP的出现打通了AI模型和外部数据库、网页API等资源&#xff0c;成倍提升工作效率。近期火山引擎团队推出了 MCP Server SDK&#xff1a; veimagex-mcp。本文介绍如何在Cherry Studio 和VSCode平台集成 veimagex-mcp。 什么是MCP MCP&#xff08;Model Context Protocol&…

掌控随心 - 服务网格的流量管理艺术 (Istio 实例)

掌控随心 - 服务网格的流量管理艺术 (Istio 实例) 想象一下,没有服务网格的时候,我们要实现像“将 1% 的用户流量导入到新版本应用”、“根据用户设备类型访问不同后端”、“模拟下游服务故障”这类高级流量策略,通常需要在代码、负载均衡器、API 网关等多个地方进行复杂且分…

[ARM][汇编] 01.基础概念

目录 1.全局标号 1.1.使用方法 1.1.1.声明全局标号 1.1.2.定义全局标号 1.1.3.引用全局标号 1.2.全局标号与局部标号的区别 1.3.注意事项 2.局部标号 2.1.使用方法 2.1.1.定义局部标号 2.1.2.跳转引用 2.2.局部标号与全局标号的对比 2.3.注意事项 3.符号定义伪指…

如何使用远程桌面控制电脑

目的&#xff1a; 通过路由器使用pc控制台式机&#xff0c;实现了有线/无线pc与台式机的双向远程桌面控制 最核心就两条&#xff1a;get ip地址与被控制机器的账户与密码。 现象挺神奇&#xff1a;被控制电脑的电脑桌面处于休眠模式&#xff0c;此时强行唤醒被控电脑会导致中断…

Hive表JOIN性能问

在处理100TB的Hive表JOIN性能问题时&#xff0c;需采用分层优化策略&#xff0c;结合数据分布特征、存储格式和计算引擎特性。以下是系统性优化方案&#xff1a; 1. 数据倾斜优化&#xff08;Skew Join&#xff09; 1.1 识别倾斜键 方法&#xff1a;统计JOIN键的分布频率&…

MongoDB 的核心概念(文档、集合、数据库、BSON)是什么?

MongoDB 是一个面向文档的数据库&#xff0c;它的核心概念与传统的关系型数据库&#xff08;RDBMS&#xff09;有所不同。以下是它的四个主要核心概念&#xff1a; 文档 (Document) 定义&#xff1a; 文档是 MongoDB 中的基本数据单元。它类似于关系型数据库中的一行记录&#…

AI智慧公园管理方案:用科技重塑市民的“夜游体验”

AI智慧公园管理方案&#xff1a;多场景智能巡检与安全防控 一、背景与痛点分析 夏季夜间&#xff0c;公园成为市民休闲娱乐的核心场所&#xff0c;但管理难度随之激增&#xff1a; 宠物管理失控&#xff1a;未牵绳宠物进入园区&#xff0c;随地排泄、惊扰游客&#xff0c;甚…

Spring Cloud Gateway 聚合 Swagger 文档:一站式API管理解决方案

前言 在微服务架构中&#xff0c;随着服务数量的增加&#xff0c;API文档管理变得越来越复杂。每个微服务都有自己的Swagger文档&#xff0c;开发人员需要记住每个服务的文档地址&#xff0c;这无疑增加了开发难度。本文将介绍如何使用Spring Cloud Gateway聚合所有微服务的Sw…

尼康VR镜头防抖模式NORMAL和ACTIVE的区别(私人笔记)

1. NORMAL 模式&#xff08;常规模式&#xff09; 适用场景&#xff1a;一般手持拍摄&#xff0c;比如人像、静物、风景或缓慢平移镜头&#xff08;如水平追拍&#xff09;等。工作特性&#xff1a; 补偿手抖引起的小幅度震动&#xff08;比如手持时自然的不稳&#xff09;&am…

Babylon.js学习之路《四、Babylon.js 中的相机(Camera)与视角控制》

文章目录 1. 引言&#xff1a;为什么相机是 3D 场景的“眼睛”&#xff1f;1.1 相机的核心作用1.2 常见相机类型概览 2. 相机基础参数解析2.1 通用属性2.2 相机坐标系 3. 详解常用相机类型3.1 自由相机&#xff08;FreeCamera&#xff09;3.2 弧形旋转相机&#xff08;ArcRotat…

【Python】普通方法、类方法和静态方法的区分

Python 中普通方法、类方法和静态方法的区分 下面我将从多个维度对这三种方法进行详细对比&#xff0c;并通过示例说明它们的使用场景和区别。 1. 核心区别总结 特性普通方法(实例方法)类方法(classmethod)静态方法(staticmethod)定义装饰器无classmethodstaticmethod第一个…

geoserver发布arcgis瓦片地图服务(最新版本)

第一步&#xff1a;下载geoserver服务&#xff0c;进入bin目录启动 需要提前安装好JDK环境&#xff0c;1.8及以上版本 安装完成&#xff0c;页面访问端口&#xff0c;进入控制台界面,默认用户名密码admin/geoserver 第二步&#xff1a;下载地图 破解版全能电子地图下载器&…

Linux服务之lvs集群与dr模式部署

目录 一.lvs相关概述 1.lvs集群的工作模式 2.lvs调度算法 3.ipvsadm工具 二.DR模式部署 一.lvs相关概述 1.lvs集群的工作模式 lvs-nat&#xff1a;修改请求报文的目标IP,多目标IP的DNAT lvs-dr&#xff1a;操纵封装新的MAC地址&#xff08;直接路由&#xff09;lvs-tu…

OFCMS代码审计-freemaker注入sql注入xxexss文件上传

环境搭建 下载地址&#xff1a;https://gitee.com/oufu/ofcms/repository/archive/V1.1.2?formatzip SSTI模板注入&#xff08;freemaker) FreeMarker模板注入实现远程命令执行 - Eleven_Liu - 博客园 在admin中找到这个 发现请求的是这个 找到他 <#assign value"f…

一键部署NSFW检测模型:快速识别并过滤敏感图片内容

以下是对nsfw_detector的简单介绍&#xff1a; nsfw_detector是一个 NSFW 内容检测器&#xff0c;支持快速docker私有部署&#xff0c;提供API服务低资源消耗&#xff0c;2GB内存即可运行该模型&#xff0c;多核CPU自动调度加速推理 - 可以识别多种文件类型&#xff1a;图片、…

【Redis】缓存穿透、缓存雪崩、缓存击穿

1.缓存穿透 是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;导致请求直接穿透缓存到达数据库&#xff0c;给数据库带来压力的情况。 常见的解决方案有两种&#xff1a; 缓存空对象&#xff1a;实现简单&#xff0c;维护方便&am…