数据结构(五)——二叉树的遍历和线索二叉树

news2024/12/26 21:55:11

5.3. 二叉树的遍历和线索二叉树

5.3.1_1 二叉树的先中后序遍历

遍历:按照某种次序把所有结点都访问一遍

二叉树的递归特性:
        ①要么是个空二叉树
        ②要么就是由“根节点+左子树+右子树”组成的二叉树

先序遍历:根左右(NLR)
中序遍历:左根右(LNR)
后序遍历:左右根(LRN)


先序遍历(PreOrder)的操作过程如下:
1. 若二叉树为空,则什么也不做;
2. 若二叉树非空:
        ①访问根结点;
        ②先序遍历左子树;
        ③先序遍历右子树。
代码实现如下:

typedef struct BiTNode{  
    ElemType data;  
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

// 先序遍历
void PreOrder(BiTree T){  
    if(T!=NULL){     
        visit(T);                 //访问根结点        
        PreOrder(T->lchild);      //递归遍历左子树 
        PreOrder(T->rchild);      //递归遍历右子树
    }
}

先序遍历—第一次路过时访问结点,图示如下

中序遍历(InOrder)的操作过程如下:
1. 若二叉树为空,则什么也不做;
2. 若二叉树非空:
        ①中序遍历左子树;
        ②访问根结点;
        ③中序遍历右子树;
代码实现如下: 

typedef struct BiTNode{  
    ElemType data;  
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

// 中序遍历
void PreOrder(BiTree T){  
    if(T!=NULL){     
        PreOrder(T->lchild);      //递归遍历左子树 
        visit(T);                 //访问根结点        
        PreOrder(T->rchild);      //递归遍历右子树
    }
}

中序遍历—第二次路过时访问结点,图示如下

后序遍历(InOrder)的操作过程如下:
1. 若二叉树为空,则什么也不做;
2. 若二叉树非空:
        ①后序遍历左子树;
        ②后序遍历右子树;
        ③访问根结点。
代码实现如下:

typedef struct BiTNode{  
    ElemType data;  
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

// 后序遍历
void PreOrder(BiTree T){  
    if(T!=NULL){     
        PreOrder(T->lchild);      //递归遍历左子树 
        PreOrder(T->rchild);      //递归遍历右子树
        visit(T);                 //访问根结点        
    }
}

后序遍历—第三次路过时访问结点 ,图示如下

// 应用:求树的深度
int treeDepth(BiTree T){  
    if(T == NULL){  
        return 0;  
    }else{      
        int l = treeDepth(T->lchild); 
        int r = treeDepth(T->rchild); 
        //树的深度=Max(左子树深度,右子树深度)+1
        return l>r ? l+1 r+1;  
    }
}

5.3.1_2 二叉树的层次遍历


算法思想:
①初始化一个辅助队列
②根结点入队
③若队列非空,则队头结点出队,访问该结点,并将其左、右孩子插入队尾(如果有的话)
④重复③直至队列为空
代码实现:

// 二叉树结点(链式存储)
typedef struct BiTNode{  
    char data;   
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

// 链式队列结点
typedef struct LinkNode{ 
    BiTNode *data;             //存结点的指针而不是结点本身
    struct LinkNode *next;
}LinkNode;

typedef struct{ 
    LinkNode *front, *rear;    //队头队尾
}LinkQueue;

// 层序遍历
void LevelOrder(BiTree T){  
    LinkQueue Q; 
    InitQueue(Q);              //初始化辅助队列
    BiTree p;
    EnQueue(Q, T);             //将根结点入队
    while(!IsEmpty(Q)){        //队列不空则循环
        DeQueue(Q, p);         //队头结点出队
        visit(p);              //访问出队结点
        if(p->lchild!=NULL)      
            EnQueue(Q, p->lchild);     //左孩子入队
        if(p->rchild!=NULL)    
            EnQueue(Q, p->rchild);     //右孩子入队
    }
}

5.3.1_3 由遍历序列构造二叉树

若只给出一棵二叉树的 前/中/后/层 序遍历序列中的一种,不能唯一确定一棵二叉树
一种遍历序列可能对应多种形态



前序 + 中序遍历序列

由前序遍历可推出根节点在中序遍历中的位置,从而确定左右结点,再依此类推

后序 + 中序遍历序列

由后序遍历可推出根结点在中序遍历中的位置,从而确定左右结点在中序遍历的位置

层序 + 中序遍历序列

由层序遍历可推出根结点,再根据根节点进一步确定左右的根的位置

5.3.2_1 线索二叉树的概念

普通二叉树进行遍历时,找前驱、后继很不方便,且每次都要从根结点出发,无法从一个指定的结点开始遍历​​​​​​​​​​​​​​。

n 个结点的二叉树,有 n+1 个空链域,可用来记录前驱、后继的信息。
指向前驱、后继的指针被称为“线索”,形成的二叉树就称为线索二叉树。



线索二叉树的存储

线索二叉树的结点在原本二叉树的基础上,新增了左右线索标志 tag。
tag == 0 时,表示指针指向孩子;tag == 1 时,表示指针是“线索”。

// 线索二叉树的结点
typedef struct ThreadNode{ 
    ElemType data;   
    struct ThreadNode *lchild, *rchild; 
    int ltag, rtag;	//左、右线索标志
}ThreadNode,*ThreadTree;

中序线索二叉树的存储

先序线索二叉树



5.3.2_2 二叉树的线索化

中序线索化

//线索二叉树结点
typedef struct ThreadNode{
    ElemType data;  
    struct ThreadNode *lchild, *rchild;   
    int ltag, rtag;            //左、右线索标志
}ThreadNode, *ThreadTree;

// 中序遍历二叉树,一边遍历一边线索化
void InThread(ThreadTree T) {
	if (T != NULL) {
		InThread(p->lchild);    //中序遍历左子树
		visit(T);               //访问根结点
		InThread(p->rchild);    //中序遍历右子树
	}
}

void visit(ThreadNode *q) {
    if(q->lchild==NULL){        //左子树为空,建立前驱线索
        q->lchild=pre;          //
        q->ltag=1;              //修改ltag=1,只有变成1才表示指针是线索
    }
    if(pre!=NULL&&pre->rchild==NULL){
        pre->rchild=q;          //建立前驱结点的后继线索
        pre->rtag=1;
    }
    pre=q;
}

//全局变量pre,指向当前访问结点的前驱
ThreadNode *pre=NULL;           //pre没有前驱,最开始指向NULL


// 中序化线索二叉树T
void CreateInThread(ThreadTree T) {
    pre = NULL;                //pre初始化为NULL
	if (T != NULL) {           //非空二叉树才能线索化
		InThread(T, pre);      //中序化线索二叉树
		if(pre->rchild = NULL)
		    pre->rtag = 1;     //处理遍历的最后一个结点
	}
}

先序线索化 

// 先序遍历二叉树T
void PreThread(ThreadTree T) {
	if (T != NULL) {           //非空二叉树才能线索化
        visit(T);              //先处理根结点
		if(T->ltag ==0)        //lchild不是前驱线索
            PreThread(T->child);
        PreThread(T->child);
  	}
}


void visit(ThreadNode *q) {
    if(q->lchild==NULL){        //左子树为空,建立前驱线索
        q->lchild=pre;          //
        q->ltag=1;              //修改ltag=1,只有变成1才表示指针是线索
    }
    if(pre!=NULL&&pre->rchild==NULL){
        pre->rchild=q;          //建立前驱结点的后继线索
        pre->rtag=1;
    }
    pre=q;
}

//全局变量pre,指向当前访问结点的前驱
ThreadNode *pre=NULL;           //pre没有前驱,最开始指向NULL

void CreateInThread(ThreadTree T) {
    pre = NULL;                //pre初始化为NULL
	if (T != NULL) {           //非空二叉树才能线索化
		PreThread(T);          //先序化线索二叉树
		if(pre->rchild = NULL)
		    pre->rtag = 1;     //处理遍历的最后一个结点
	}
}

后序线索化 

// 后序遍历二叉树T
void PostThread(ThreadTree T) {
	if (T != NULL) {           //非空二叉树才能线索化
		PreThread(T->child);   //后序遍历左子树
        PreThread(T->child);   //后序遍历右子树
        visit(T);              //访问根结点
  	}
}


void visit(ThreadNode *q) {
    if(q->lchild==NULL){        //左子树为空,建立前驱线索
        q->lchild=pre;          //
        q->ltag=1;              //修改ltag=1,只有变成1才表示指针是线索
    }
    if(pre!=NULL&&pre->rchild==NULL){
        pre->rchild=q;          //建立前驱结点的后继线索
        pre->rtag=1;
    }
    pre=q;
}

//全局变量pre,指向当前访问结点的前驱
ThreadNode *pre=NULL;           //pre没有前驱,最开始指向NULL

//后续线索化二叉树T
void CreateInThread(ThreadTree T) {
    pre = NULL;                //pre初始化为NULL
	if (T != NULL) {           //非空二叉树才能线索化
		PostThread(T);          //后续线索化二叉树
		if(pre->rchild = NULL)
		    pre->rtag = 1;     //处理遍历的最后一个结点
	}
}

5.3.2_3 在线索二叉树中找前驱后继

在中序线索二叉树中找到指定结点*p 的中序后继 next  
①若 p->rtag==1,则 next = p->rchild
②若 p->rtag==0,则 next = p 的右子树中最左下结点

// 找到以p为根的子树中,第一个被中序遍历的结点
ThreadNode *FirstNode(ThreadNode *p){
    // 循环找到最左下结点(不一定是叶结点)
    while(p->ltag==0)
        p=p->rchild;
    return p;
}

// 在中序线索二叉树中找到结点p的后继结点
ThreadNode *NextNode(ThreadNode *p){
    // 右子树中最左下的结点
    if(p->rtag==0)
        return FirstNode(p->lchild);
    else
        return p->rchild;    //rtage==1直接返回后继线索
}

// 对中序线索二叉树进行中序循环(利用线索实现的非递归方法) 空间复杂度O(1)
void InOrder(ThreadNode *T){
    for(ThreadNode *p=FirstNode(T); p!=NULL; p=NextNode(p))
        visit(p);
}

在中序线索二叉树中找到指定结点*p 的中序前驱 pre
①若 p->ltag==1,则 pre = p->lchild
②若 p->ltag==0

// 找到以p为根的子树中,最后一个被中序遍历的结点
ThreadNode *LastNode(ThreadNode *p){
    // 循环找到最右下结点(不一定是叶结点)
    while(p->rtag==0)
        p=p->rchild;
    return p;
}

// 在中序线索二叉树中找到结点p的前驱结点
ThreadNode *PreNode(ThreadNode *p){
    // 左子树中最右下的结点
    if(p->ltag==0)
        return LastNode(p->lchild);
    else
        return p->lchild;        //ltage==1直接返回前驱线索
}

// 对中序线索二叉树进行中序循环(非递归方法实现)
void RevOrder(ThreadNode *T){
    for(ThreadNode *p=LastNode(T); p!=NULL; p=PreNode(p))
        visit(p);
}

在先序线索二叉树中找到指定结点*p 的先序后继 next
①若 p->rtag==1,则 next = p->rchild
②若 p->rtag==0

在先序线索二叉树中找到指定结点*p 的先序前驱 pre
①若 p->ltag==1,则 next = p->lchild
②若 p->ltag==0

在后序线索二叉树中找到指定结点*p 的后序前驱 pre
①若 p->ltag==1,则 pre = p->lchild
②若 p->ltag==0

在后序线索二叉树中找到指定结点*p 的后序后继 next
①若 p->rtag==1,则 next = p->rchild
②若 p->rtag==0

​​​​​​​

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

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

相关文章

深度学习中的随机种子random_seed

解释 由于模型中的参数初始化例如权重参数如下图,就是随机初始化的,为了能够更好的得到论文中提到效果,可以设置随机种子,从而减少算法结果的随机性,使其接近于原始结果。 设置了随机种子,产生的随机数都…

硬核分享|AI语音识别转文字与自动生成字幕

硬核分享|AI语音识别转文字与自动生成字幕_哔哩哔哩_bilibili 在现代快节奏的生活中,语音转文字工具成为了我们工作和学习中的得力助手。它能够将我们说出的话语迅速转化为文字或者将语音视频自动生成字幕,提供便捷和高效。 语音转文字转字幕工具是一种…

spring boot商城、商城源码 欢迎交流

一个基于spring boot、spring oauth2.0、mybatis、redis的轻量级、前后端分离、防范xss攻击、拥有分布式锁,为生产环境多实例完全准备,数据库为b2b2c设计,拥有完整sku和下单流程的商城 联系: V-Tavendor

计算机网络:分层体系结构

计算机网络:分层体系结构 基本分层概述各层次的任务物理层数据链路层网络层运输层应用层 数据传递过程分层体系常见概念实体协议服务 基本分层概述 为了使不同体系结构的计算机网络都能互联,国际标准化组织于 1977 年成立了专门机构研究该问题。不久他们…

不启动BMIDE,Teamcenter如何查看property的real property name

问题描述: Teamcenter客户端,查看Item 属性,属性名称默认显示的是Display Name。 在各类开发过程中,对属性的操作,需要使用real property name才能进行。开发可能不在server端,没有安装BMIDE,如…

4核16G服务器租用优惠价格,26.52元1个月,半年149元

阿里云4核16G服务器优惠价格26.52元1个月、79.56元3个月、149.00元半年,配置为阿里云服务器ECS经济型e实例ecs.e-c1m4.xlarge,4核16G、按固定带宽 10Mbs、100GB ESSD Entry系统盘,活动链接 aliyunfuwuqi.com/go/aliyun 活动链接打开如下图&a…

Linux设备驱动开发 - 三色LED呼吸灯分析

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 展锐UIS7885呼吸灯介绍呼吸灯调试方法亮蓝灯亮红灯亮绿灯展锐UIS7885呼吸灯DTS配置ump9620 PMIC驱动ump9620中的LED呼吸灯驱动LED的tr…

python框架的一加剧场管理系统的设计与实现flask-django-nodejs-php

本文讲述了一加剧场管理系统。结合电子管理系统的特点,分析了一加剧场管理系统的背景,给出了一加剧场管理系统实现的设计方案。 本论文主要完成不同用户的权限划分,不同用户具有不同权限的操作功能,在用户模块,主要有用…

数据挖掘与分析学习笔记

一、Numpy NumPy(Numerical Python)是一种开源的Python库,专注于数值计算和处理多维数组。它是Python数据科学和机器学习生态系统的基础工具包之一,因为它高效地实现了向量化计算,并提供了对大型多维数组和矩阵的支持…

开源数据集 nuScenes 之 3D Occupancy Prediction

数据总体结构 Nuscenes 数据结构 可以看一下我的blog如何下载完整版 mmdetection3d ├── mmdet3d ├── tools ├── configs ├── data │ ├── nuscenes │ │ ├── maps │ │ ├── samples │ │ ├── sweeps │ │ ├── lidarseg (o…

PHP 读取嵌入式数据 SQLite3

SQLite3 属于轻量级开源的嵌入式关系型数据库,但它支持 ACID(Atomicity,Consistency,Isolation,Durability) 事务。 SQLite Download Page: https://www.sqlite.org/download.html 第一步:在 php.ini 中开启 extensionsqlite3 第二步:连接数…

K8s+Nacos实现应用的优雅上下线【生产实践】

文章目录 前言一、环境描述二、模拟请求报错三、配置优雅上下线1.修改nacos配置2.修改depolyment配置3.重新apply deployment后测试4.整体(下单)测试流程验证是否生效 四、期间遇到的问题 前言 我们在使用k8s部署应用的时候,虽然k8s是使用滚动升级的,先…

畅谈AIGC,ISIG-AIGC技术与应用发展峰会成功举办

3月16日,第四届ISIG中国产业智能大会在上海中庚聚龙酒店如期开幕,此次大会由苏州市金融科技协会指导、企智未来科技(RPA中国、LowCode低码时代、AIGC开放社区)主办。大会聚集了来自不同领域的专家学者、行业领军人物及技术研发者&…

MySQL 更新执行的过程

优质博文:IT-BLOG-CN Select语句的执行过程会经过连接器、分析器、优化器、执行器、存储引擎,同样的 Update语句也会同样走一遍 Select语句的执行过程。 但是和 Select最大不同的是,Update语句会涉及到两个日志的操作redo log(重做…

python的街道办管理系统flask-django-php-nodejs

随着世界经济信息化、全球化的到来和互联网的飞速发展,推动了各行业的改革。若想达到安全,快捷的目的,就需要拥有信息化的组织和管理模式,建立一套合理、动态的、交互友好的、高效的街道办管理系统。当前的信息管理存在工作效率低…

3.22Code

基于邻接矩阵的新顶点的增加 #include<iostream>using namespace std;#define MAXVNUM 100typedef struct{int vexsNum;int arcsNum;int arcs[MAXVNUM][MAXVNUM];int vexs[MAXVNUM]; }AdjMatrixG;void InitGraph(AdjMatrixG &G){for(int k0;k<MAXVNUM;k)G.vexs[k…

webgl浏览器渲染设置

在浏览器中程序图形化webgl渲染时&#xff0c;有时候发现代码没有问题&#xff0c;但是就是无法渲染或者渲染报错&#xff0c;此时可以尝试如下的设置&#xff1a; 通过在chrome浏览器输入chrome&#xff1a;//flags打开扩展 设置一&#xff08;webgl开发者扩展&#xff09; 设…

计算机网络——数据链路层(数据链路层功能概述)

计算机网络——数据链路层&#xff08;数据链路层功能概述&#xff09; 数据链路层的功能数据链路层的基本概念封装成帧和透明传输 我们之前已经学完了物理层的所有内容&#xff0c;今天开始我们要进入数据链路层的学习&#xff0c;如果有小伙伴对物理层的内容感兴趣的话&#…

Xilinx FPGA 远程升级时bin和bit文件使用注意

以Spartan-6 ISE开发环境为例。 ISE开发环境支持生成bit和bin格式的程序文件&#xff0c;可以在生成选项进行配置&#xff1a; 把生成的bit文件和bin文件进行二进制比较&#xff0c;发现bit比bin文件头部多了一些内容&#xff08;头部信息&#xff09;&#xff0c;剩余部分完…

苹果电脑不能删除移动硬盘文件 苹果电脑移动硬盘只读模式如何更改 移动硬盘文件或目录损坏且无法读取怎么办

当我们将移动硬盘插入苹果电脑后&#xff0c;发现无法对移动硬盘中的文件进行编辑该怎么办&#xff1f;相信有不少网友遇到过这类情况。苹果电脑不能删除移动硬盘文件&#xff0c;或无法拷贝硬盘里的文件。今天我为大家解决苹果电脑移动硬盘只读模式如何更改的问题&#xff0c;…