15-数据结构-二叉树的遍历,递归和非递归

简介:
        本文主要是代码实现,二叉树遍历,递归和非递归(用栈)。主要为了好理解,直接在代码处,加了详细注释,方便复习和后期默写。主要了解其基本思想,为后期熟练应用打基础。

遍历的意义,就是为了实现在二叉树上,进行各种操作,给每个结点都光顾到位,到根节点时,进行当前节点的操作。


目录:

目录

一、前序遍历。

1.1前序遍历—递归

1.2前序遍历—非递归

二、中序遍历

2.1中序遍历—递归

2.2中序遍历—非递归

三、后序遍历

3.1后序遍历—递归

3.2后序遍历—非递归

   五、总代码

5.1代码

5.2运行结果图


一、前序遍历。

1.1前序遍历—递归

        简介:前序为:先访问根结点,再访问其左孩子,再访问右孩子(根左右)。

//前序遍历,递归 
void PreOrder(BTNode *node)
{if(node==NULL)//当前结点为空时,返回上一层递归空间 {printf("#");return;}//结点非空时 visit(node);PreOrder(node->lchild);PreOrder(node->rchild);
}

1.2前序遍历—非递归

        简介:非递归,就是利用栈(就是一个存放树结点指针的数组,再加一个栈顶标记top),存放树节点的指针。树不为空的时候先入栈,随后,栈不为空时,再进行出栈操作。前序遍历出栈时,先出栈后,先访问该节点信息,随后再判断该节点是否有右孩子,有则,右孩子的指针存进栈中。再判断是否有左孩子,有则左孩子指针存进栈,

//前序遍历,非递归 
void Stack_PreOrder(BTNode *node)
{if(node==NULL)//树为空,不处理return;//创建一个栈,存放树结点类型的地址 BTNode* Stack[10];int top=-1;//工作指针,随着p指针,记录树的当前结点位置 BTNode *p=NULL;//当树非空时,进行操作 if(node !=NULL){//入栈 top++;Stack[top]=node;//随后进行出栈操作,只有栈非空时,才可出栈 while(top != -1){//取出此时栈顶元素 p=Stack[top];top--;//然后进行访问当前结点的相关操作 visit(p);//访问完根,在看该根的右孩子,入栈 ,因为是栈,先进后出,而前序为根左右,根出来后,右入栈,之后左入栈,最后出栈是栈顶出 if(p->rchild!=NULL){top++;Stack[top]=p->rchild;}//访问完右孩子,在看该根的左孩子,入栈 if(p->lchild!=NULL){top++;Stack[top]=p->lchild;}			}		}
}

二、中序遍历

2.1中序遍历—递归

        简介:左根右。不理解为啥的,可以画图,每进入一个新的函数,便是一个新的空间。

//中序遍历-递归 
void InOrder(BTNode *node)
{if(node==NULL){printf("#");return;}InOrder(node->lchild);visit(node);InOrder(node->rchild);
}

2.2中序遍历—非递归

        简介:其实,栈也好,递归也罢,需要操作的,仅为两步,第一步为进入新树的一些列操作。操作完,进入第二步,进到另一方向孩子树中,该树中的操作,还是先进性第一步,再进行第二部,

        思想:中序遍历非递归操作,最外圈来个do-while循环,先执行,再判断。如果栈内非空,或者该结点不为空,都进行中序遍历操作。

        do-while里面的操作:先左子树操作:一直遍历,入栈元素,随后给指针地址换成该节点的左孩子,就是一直遍历到左孩子为空,才停止。至此,左根右中的左操作完毕。随后出栈元素,进行左根右中的根操作,访问根节点。至此,为第一步的操作。随后第二部,进入方向的树中,即结点指针换为右孩子地址,

//中序遍历-非递归
void StackInOrder(BTNode *node)
{if(node==NULL)//树为空,则不处理return;printf("中序遍历-非递归:");BTNode* p=node;BTNode* Stack[10];int top=-1;do{//当结点不为空时,入栈,并进入左孩子。 ——访问左孩子 while(p!=NULL){top++;Stack[top]=p;p=p->lchild;}//一直遍历左,遍历到空,此时,出栈p=Stack[top];top--;visit(p);//访问根 p=p->rchild;//根访问完,随后,访问右孩子。随后,右孩子中,又是新的树,然后再进行左根右操作,形成循环,从上面再来一圈。 }while(top!=-1 || p!=NULL);//只要树不为空,或者栈内有元素,就一直进行操作。 } 

三、后序遍历

3.1后序遍历—递归

        简介:左右根。

// 后序遍历-递归
void PostOrder(BTNode *node)
{if(node==NULL){printf("#");return;}PostOrder(node->lchild);PostOrder(node->rchild);visit(node);
}

3.2后序遍历—非递归

        简介:这个比较麻烦,不过还是利用描边法去做,根据描边法,根节点被访问两次,第一次时入栈时,第二次时判断是否出栈时,就看从那一层返回到根节点的,如果从右孩子返回的,则进行出栈操作,先记录当前结点,再出栈。否则,则进行右子树结点的出栈,

        这里面,跟中序,略有不同,入栈和出栈的情况需要判断,所以需要用栈顶指针时刻对比。

先跟根结点入栈,随后当栈内不为空时,一直进行遍历操作。先进性第一步的入栈操作(当上层遍历,即不是栈顶指针的左孩子又不是右孩子时,更新工作指针为左孩子,随后进行一直左孩子入栈操作)第二步,左孩子到底了,此时需要面临出栈,因此给当前栈顶元素取出来,如果该树没有左孩子,或者pre与右孩子地址相同,则进行出栈操作,并记录出栈前的指针p,否则则给右孩子入栈。

void StackPostOrder(BTNode *node)
{printf("后序遍历-非递归:");if(node==NULL)return; BTNode *p=node;//工作指针 BTNode *pre=NULL;//表示上层结点位置 //栈 BTNode *Stack[10];int top=-1;//先跟根节点入栈,为了方便第一次判断top++;Stack[top]=p;do{//先判断上层结点是否遍历过,没有,则进行左子树都入栈,入到底if(pre!=Stack[top]->lchild && pre!=Stack[top]->rchild){p=Stack[top]->lchild;//上次没有遍历过左右孩子,那么开始栈顶元素的左孩子入栈操作。while(p!=NULL){top++;Stack[top]=p;p=p->lchild;	}	}//左孩子方向弄到底后,开始判断,是否需要出栈输出。p=Stack[top];//记录此时的栈顶元素if(p->rchild==NULL || pre==p->rchild)//如果右孩子为空,或者上一层和当前结点的右孩子相等,则输出 {pre=p;//记录当前结点地址 visit(p);//输出 top--;//输出了,栈内指针减少 }else{top++;Stack[top]=p->rchild;//右孩子入栈	} }while(top!=-1); 
}

   五、总代码

5.1代码

#include <stdio.h>
#include <stdlib.h>
//创建树,孩子链表 
typedef struct BTNode
{int data;struct BTNode *rchild,*lchild;}BTNode; 
//创建树结点,并初始化
BTNode* BuyNode(int x)
{BTNode* node=(BTNode*)malloc(sizeof(BTNode));node->data=x;node->lchild=NULL;node->rchild=NULL;return node;	
} 
//手动创建树
BTNode* CreatTree()
{BTNode* node1=BuyNode(1);BTNode* node2=BuyNode(2);BTNode* node3=BuyNode(3);BTNode* node4=BuyNode(4);BTNode* node5=BuyNode(5);node1->lchild=node2;node1->rchild=node3;node2->lchild=node4;node2->rchild=node5;return node1;		
} 
//访问当前结点时的操作 
void visit(BTNode *node)
{printf("%d",node->data);	
} 
//前序遍历,递归 
void PreOrder(BTNode *node)
{if(node==NULL)//当前结点为空时,返回上一层递归空间 {printf("#");return;}//结点非空时 visit(node);PreOrder(node->lchild);PreOrder(node->rchild);
}
//前序遍历,非递归 
void Stack_PreOrder(BTNode *node)
{if(node==NULL)return;printf("前序遍历-非递归:");//创建一个栈,存放树结点类型的地址 BTNode* Stack[10];int top=-1;//工作指针,随着p指针,记录树的当前结点位置 BTNode *p=NULL;//当树非空时,进行操作 if(node !=NULL){//入栈 top++;Stack[top]=node;//随后进行出栈操作,只有栈非空时,才可出栈 while(top != -1){//取出此时栈顶元素 p=Stack[top];top--;//然后进行访问当前结点的相关操作 visit(p);//访问完根,在看该根的右孩子,入栈 ,因为是栈,先进后出,而前序为根左右,根出来后,右入栈,之后左入栈,最后出栈是栈顶出 if(p->rchild!=NULL){top++;Stack[top]=p->rchild;}//访问完右孩子,在看该根的左孩子,入栈 if(p->lchild!=NULL){top++;Stack[top]=p->lchild;}			}		}
}
//中序遍历-递归 
void InOrder(BTNode *node)
{if(node==NULL){printf("#");return;}InOrder(node->lchild);visit(node);InOrder(node->rchild);
}
//中序遍历-非递归
void StackInOrder(BTNode *node)
{if(node==NULL)return;printf("中序遍历-非递归:");BTNode* p=node;BTNode* Stack[10];int top=-1;do{//当结点不为空时,入栈,并进入左孩子。 ——访问左孩子 while(p!=NULL){top++;Stack[top]=p;p=p->lchild;}//一直遍历左,遍历到空,此时,出栈p=Stack[top];top--;visit(p);//访问根 p=p->rchild;//根访问完,随后,访问右孩子。随后,右孩子中,又是新的树,然后再进行左根右操作,形成循环,从上面再来一圈。 }while(top!=-1 || p!=NULL);//只要树不为空,或者栈内有元素,就一直进行操作。 }
// 后序遍历-递归
void PostOrder(BTNode *node)
{if(node==NULL){printf("#");return;}PostOrder(node->lchild);PostOrder(node->rchild);visit(node);
}
//后序遍历-非递归
void StackPostOrder(BTNode *node)
{printf("后序遍历-非递归:");if(node==NULL)return; BTNode *p=node;//工作指针 BTNode *pre=NULL;//表示上层结点位置 //栈 BTNode *Stack[10];int top=-1;//先跟根节点入栈,为了方便第一次判断top++;Stack[top]=p;do{//先判断上层结点是否遍历过,没有,则进行左子树都入栈,入到底if(pre!=Stack[top]->lchild && pre!=Stack[top]->rchild){p=Stack[top]->lchild;//上次没有遍历过左右孩子,那么开始栈顶元素的左孩子入栈操作。while(p!=NULL){top++;Stack[top]=p;p=p->lchild;	}	}//左孩子方向弄到底后,开始判断,是否需要出栈输出。p=Stack[top];//记录此时的栈顶元素if(p->rchild==NULL || pre==p->rchild)//如果右孩子为空,或者上一层和当前结点的右孩子相等,则输出 {pre=p;//记录当前结点地址 visit(p);//输出 top--;//输出了,栈内指针减少 }else{top++;Stack[top]=p->rchild;//右孩子入栈	} }while(top!=-1); 
}
int main()
{BTNode* root=CreatTree();//前序遍历打印printf("前序遍历-递归:"); PreOrder(root);//递归 printf("\n"); Stack_PreOrder(root);//非递归,栈来做 printf("\n"); printf("中序遍历-递归:");InOrder(root); printf("\n"); StackInOrder(root); printf("\n"); printf("后续遍历-递归:");PostOrder(root);printf("\n"); StackPostOrder(root);return 0;} 

5.2运行结果图

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

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

相关文章

光伏电站、变电站、等直流系统电参量测量仪器怎么选型

安科瑞虞佳豪 壹捌柒陆壹伍玖玖零玖叁 应用场景 工作拓扑图 功能 ①对电能参数进行采样计量和监测&#xff0c;逆变器或者能量管理系统&#xff08;EMS&#xff09;与之进行通讯&#xff0c;根据实时功率及累计电能实现防逆流、调节发电量、电池充放电等功能&#xff1b; ②…

uniapp使用sqlite 数据库

uniapp使用sqlite 数据库 傻瓜式使用方式&#xff0c;按步骤&#xff0c;即可使用。 1.开启sqlite 在项目中manifest.json该文件中配置 2.封装数据库的调用方法 const sqlName "zmyalh" //定义的数据库名称 const sqlPath "_doc/zmyalh.db" //定义数…

three.js(九):内置的路径合成几何体

路径合成几何体 TubeGeometry 管道LatheGeometry 车削ExtrudeGeometry 挤压 TubeGeometry 管道 TubeGeometry(path : Curve, tubularSegments : Integer, radius : Float, radialSegments : Integer, closed : Boolean) path — Curve - 一个由基类Curve继承而来的3D路径。 De…

mysql 查看 、设置缓冲池 buffer_pool

Mysql 存储引擎 MyIsam 和 Innodb 引擎 myIsam 存储引擎&#xff1a; 只缓存索引&#xff0c;不缓存数据&#xff0c;对应的键缓存参数为 key_buffer_size show variables like ‘key_buffer_size’; set global key_buffer_sizexxxx; 或者 my.ini my.cnf [server] key_buffer…

Mac下Docker Desktop安装命令行工具、开启本地远程访问

Mac系统下&#xff0c;为了方便在terminal和idea里使用docker&#xff0c;需要安装docker命令行工具&#xff0c;和开启Docker Desktop本地远程访问。 具体方法是在设置-高级下&#xff0c; 1.将勾选的User调整为System&#xff0c;这样不用手动配置PATH即可使用docker命令 …

2023第七届蓝帽杯 初赛 web LovePHP

LovePHP 直接给了源码。 network查看到&#xff0c;PHP版本是7.4.33 题目要求我们GET一个my_secret.flag参数&#xff0c;根据PHP字符串解析特性&#xff0c;PHP需要将所有参数转换为有效的变量名&#xff0c;因此在解析查询字符串时&#xff0c;它会做两件事&#xff1a; 删…

飞腾PSPA可信启动--1 非对称加密

最近计划在梳理下飞腾安全平台架构PSPA&#xff0c;就先从可信启动开始吧。有一些基础的密码学知识&#xff0c;各位在研究可信固件的时候可能会有一些不解&#xff0c;为了便于大家理解&#xff0c;编了几个小故事&#xff0c;希望能够帮助大家理清其中的各种关系。目前可信启…

【人工智能】—_逻辑回归分类、对数几率、决策边界、似然估计、梯度下降

文章目录 逻辑回归分类Logistic Regression ClassificationLogistic Regression: Log OddsLogistic Regression: Decision BoundaryLikelihood under the Logistic ModelTraining the Logistic ModelGradient Descent 逻辑回归分类 考虑二分类问题&#xff0c;其中每个样本由一…

【AI辅助办公】PDF转PPT,移除水印

PDF转PPT 将PDF上传链接即可转换成PPT。​​​​​​ ​​​​​​​ https://www.camscanner.com/pdftoppthttps://www.camscanner.com/pdftoppt​​​​​​​​​​​​​​移除水印 第一步&#xff1a;打开视图-宏 第二步&#xff1a;输入宏名&#xff08;可以是人以文字…

「快学Docker」Docker容器安全性探析

「快学Docker」Docker容器安全性探析 引言容器安全性威胁Docker容器安全性目录容器镜像安全性主机与容器隔离访问控制运行时监控与防御网络安全性Docker容器安全性最佳实践 总结 引言 在当今快速发展的软件开发和部署领域&#xff0c;容器化技术已经成为一种不可或缺的工具。然…

【App端】uni-app使用百度地图api和echarts省市地图下钻

目录 前言方案一&#xff1a;echarts百度地图获取百度地图AK安装echarts和引入百度地图api完整使用代码 方案二&#xff1a;echarts地图和柱状图变形动画实现思路完整使用代码 方案三&#xff1a;中国地图和各省市地图下钻实现思路完整使用代码 前言 近期的app项目中想加一个功…

中小企业想出圈,媒介盒子教你9个营销技巧

无论是企业还是品牌营销人员&#xff0c;要想脱颖而出&#xff0c;必须从品牌意识、品牌思维、品牌策略和品牌战术多个维度做好品牌运营。盒子总结的9个品牌营销新趋势&#xff0c;拿走不谢&#xff01;01 和名导明星合作企业品牌明星化能树立较高的知名度和美誉度&#xff0c;…

Rabbitmq消息积压问题如何解决以及如何进行限流

一、增加处理能力 优化系统架构、增加服务器资源、采用负载均衡等手段&#xff0c;以提高系统的处理能力和并发处理能力。通过增加服务器数量或者优化代码&#xff0c;确保系统能够及时处理所有的消息。 二、异步处理 将消息的处理过程设计为异步执行&#xff0c;即接收到消息…

OpenGL-入门-BMP像素图glReadPixels(2)保存显示的界面

用glReadPixels保存显示的界面 #include <GL/glut.h> #include <iostream> #include <fstream> #include <vector>// Save pixel data as BMP image void saveBMP(const std::string& filename, int width, int height, const std::vector<GLu…

多项式求逆

已知 F F F&#xff0c;求 G G G 考虑倍增 F ( x ) ∗ H ( x ) ≡ 1 ( m o d x n / 2 ) F(x) * H(x) \equiv 1 \pmod{x^{n/2}} F(x)∗H(x)≡1(modxn/2) F ( x ) ∗ G ( x ) ≡ 1 ( m o d x n / 2 ) F(x) * G(x) \equiv 1 \pmod{x^{n/2}} F(x)∗G(x)≡1(modxn/2) 假设 H H…

Shell编程之流程控制

目录 if判断 case语句 for循环 while循环 if判断 语法&#xff1a; if [ 条件判断表达式 ] then 程序 elif [ 条件判断表达式 ] then 程序 else 程序 fi 注意&#xff1a; [ 条件判断表达式 ]&#xff0c;中括号和条件判断表达式之间必须有空格。if&#xff0c;elif…

自然语言处理(NLP)是什么?

NLP(自然语言处理) 和 Phoebe Liu 的简介 您有没有和聊天机器人互动过&#xff1f;或者您是否向虚拟助手&#xff0c;例如 Siri、Alexa 或您车上的车载娱乐系统发出过某些请求&#xff1f;您使用过在线翻译吗&#xff1f;我们大多数人都曾与这些人工智能 (AI) 互动过&#xff…

某人事系统架构搭建设计记录

首发博客地址 https://blog.zysicyj.top/ 先大致列一下基础情况 架构必须是微服务 场景上涉及大量查询操作&#xff0c;分析操作 存在临时大量写入的场景 并发并不高 对高可用要求较高&#xff0c;不能挂掉 对安全要求高 要能过等保测试等三方测试 使用人数并不多&#xff0c;十…

git rebase和merge区别

一、概述 merge和rebase 标题上的两个命令&#xff1a;merge和rebase都是用来合并分支的。 这里不解释rebase命令&#xff0c;以及两个命令的原理&#xff0c;详细解释参考这里。 下面的内容主要说的是两者在实际操作中的区别。 1.1 什么是分支 分支就是便于多人在同一项目…

基于流计算 Oceanus(Flink) CDC 做好数据集成场景

由于第一次做实时&#xff0c;所以踩坑比较多&#xff0c;见谅(测试环境用的flink),小公司没有用到hadoop组件 一、踩坑记录 1:本地代码的flink版本是flink1.15.4&#xff0c;生产环境是flink1.16.1&#xff0c;在使用侧输出流时报错&#xff0c;需要使用以下写法,需要使用Si…