二叉树先序遍历,中序遍历,后序遍历,层次遍历学习总结及完整C/C++代码

伪代码阐述

先序遍历

先序遍历:先访问根节点, 然后深入左子树,直到不能深入时再深入右子树
由定义可得递归式

void travPre_R(BinNodePosi* x,VISIT& visit){if(!X) return; //到达叶子节点,开始回归visit(x->data);//向左子树深入的过程中便开始进行对每个节点的数据进行访问travPre_R(x->lChild,visit);//深入右子树travPre_R(x->rChild,visit);//深入右子树
}

例:
这里写图片描述
其遍历顺序为: 0-1-3-7-8-4-9-10-2-5-11-12-6-13-14
就上述顺序表明遍历过程总是从根到左,再到右
由消除尾递归的方式可得其迭代式

void travPre_I1(BinNodePosi* x,VISIT& visit){stack<BinNodePosi>s; //借助辅助栈记录根节点的左右孩子,以便于回溯if(x) s.push(x);while(!s.empty()){visit(s.top()->data);//访问当前节点s.pop();//删除栈顶if(x->lChild) s.push(x->rchild);//由于栈的结构,所以让rChild先进,lChild后进即可做到先访问lChild,后rChildif(x->rChild) s.push(x->lChild);}//整个while操作就可以实现向左子树深入的过程,直到没有左子树
}

通过消除尾递归的方式所得到的迭代式不具有普遍性(无法推广到中序,后序,这二者非尾递归),就此通过以下迭代式做阐述

void travPre_I2(BinNodePosi* x,VISIT& visit){stack<BinNodePosi>s;while(x){//visit(x->data);while(x->lChild){s.push(x->rChild);//有右孩子存入栈中,以便回溯访问x=x->lchild;//迭代向左子树进行深入}x=s.top();s.pop();}//看似O(n^2),但复杂度低于递归版
}

中序遍历

中序遍历:先查看最左端的叶子节点,然后根节点,最后右子树
得递归式:

void traveIn_R(BinNodePosi* x,VISIT& visit){if(!x) return;traveIn_R(x->lChild,visit);//先向左深入直到最左的叶子节点visit(x->data);traveIn_R(x->lChild,visit);
}

这里写图片描述
上图的中序遍历顺序:7-3-8-1-9-4-10-0-11-5-12-2-13-6-14
根据先序遍历的迭代可推广到中序遍历

void traveIn_I(BinNodePosi* x,VISIT& visit){stack<BinNodePosi>s;while(!s.empty()){while(x){s.push(x);x=x->lChild;//向左子树不断深入}x=s.top();visit(x->data);x=x->rChild;s.pop();}
}

上述过程描述如下:
先将所有最左边的左子树存入栈中,直到到达最左端的叶子节点(如上图的7号位置),当进行完visit操作后,节点转向7号的右子树,没有右子树的情况下将弹出栈内元素,开始回溯,向上回溯至当前节点具有右子树时,将该右子树下的所有左子树入栈,重复上述操作,直到所有节点都遍历.
遍历过程中无右子树的节点需要做回溯处理

后序遍历

后序遍历: 先依次遍历完左子树,右子树后才访问根节点
得如下递归定义式:

void travePost_R(BinNodePosi* x,VISIT& visit){if(!x) return;travePost_R(x->lChild,visit);travePost_R(x->rChild,visit);visit(x->data);
}

这里写图片描述

上述图片中的遍历顺序:7-8-3-9-10-4-1-11-12-5-13-14-6-2-0
迭代式如下:

void travePast_I(BinNodePosi* x,VISIT& visit){stack<BinNodePosi>s;if(x)s.push(x);while(!s.empty()){if(s.top()!=x->parent){//s.top()!=x->parent主要用于判断两节点是否属于兄弟节点,例如上图的3,4;使得可以转向4号,将4号下的最左边的左子树及部分右子树进行入栈处理while(x){if(x->rChild) s.push(x->rChild);if(x->lChild) s.push(x->lChild);x=x->lChild;//将最左边的左子树,及部分右子树依次存入栈中}}x=s.top();visit(x->data);s.pop();}
}

层次遍历

层次遍历:将二叉树看做金字塔形状,层次遍历做到的就是对每一层中左子树,右子树进行遍历处理,遍历顺序总是"先上后下,先左后右", 该遍历方式在广度优先遍历中也有所应用
由于层次遍历具有顺序性,而非后进先出原则,所以采用queue处理
上图中的遍历顺序:0-1-2-3-4-5-6-7-8-9-10-11-12-13-14
迭代式如下:

void traveLevel_R(BinNodePosi* x,VISIT& visit){queue<BinNodePosi>q;if(x)  q.push(x);while(!q.empty()){x=queue.front();visit(x->data);if(x->lChild) q.push(x->lChild);if(x->rChild) q.push(x->rChild);}	
}

每当一个节点入队时,便让该节点的左右子树入队,由于队列的先进先出的性质,就能使每一层都能依次遍历

来一点小插曲

完全二叉树
在层次遍历中,每一次迭代中都必定有一个节点出队,如果在前n/2次迭代中都有左孩子入队, 且前n/2-1次迭代中都有右孩子入队,那么这棵树就是完全二叉树
拓扑结构特征: 叶子节点只能出现在最底部的两层,且最底层的叶子节点必须位于次叶子节点的左侧
高度h的完全二叉树, 规模介于2h~2(h+1)-1之间

满二叉树
所有叶子节点都位于最底层, 高度为h的满二叉树由2^(h+1)-1个节点组成

完整详细设计(递归的设计方式很简单将不再阐述)

数据结构定义如下:

二叉树本身主要由结构体完成, 该结构体包含每个节点存储的节点数据value, 以及两个左右指针 lchild 和 rchild和一个父指针 parent , 这两个指针主要用来指向二叉树的左右子树, 通过左右指针, 就能很好的完成二叉树的遍历, 使用父指针也能很好完成节点的记录, 由于采用的非递归的方式遍历树结构, 所以需要使用到辅助数组, 通过数组, 和循环模拟递归遍历的过程。

功能详细设计:

  • 二叉树结构:每一个二叉树节点包含数据域以及3个指针(分别指向左子树,右子树,父节点)。
  • 创建二叉树: 采用前序遍历的方式创建一棵完整的二叉树, 便于后面的遍历方式的测试,我使用"#"字符代表空。
  • 非递归的前序遍历: 前序遍历的方式是先查看当前节点内容然后再查看左子树, 最后查看右子树。所以在非递归遍历中, 就模拟这种遍历方式, 首先存入根节点, 使用循环, 每次将数组最后位置的节点取出, 进行访问, 将访问过的节点的右子树存入数组中, 然后存左子树, 循环终止条件是数组为空, 模拟这种情况, 就能每次做到先访问根, 然后访问左子树, 最后访问右子树
  • 非递归中序遍历: 中序遍历是先访问左子树, 再访问根节点, 最后访问右子树, 同样的采用数组模拟这种遍历方式。首先将当前节点的所有依次左子树存入数组中(直到没有左子树),然后取出数组中最后一个位置的节点进行访问,此时再转向右子树(无论右子树是否存在,这里需要利用右子树等于空的条件进行判断下一次循环是否要再将左子树存入数组中),执行完上述操作后,再重复执行,直到所有节点都访问完。
  • 非递归后序遍历:后序遍历是先访问左子树,再访问右子树,最后访问根节点,非递归实现:将当前节点的所有左子树依次存入数组中(同时将该节点的右子树存入数组中,便于待会模拟回溯),左右子树存储的条件是:左右节点都访问过才能访问父节点,当栈顶元素与当前元素的父节点是同一个,则说明当前节点存在兄弟节点还没被访问1.整个过程尽量往左子树方向走,如果该节点没有左子树,就往右子树方向走一次,然后重复步骤1,直到节点左右子树都不存在,此时输出该节点存储的数据

函数关系调用图如下:
在这里插入图片描述

写代码容易出现的问题:

正确理解递归遍历的方式, 防止理解错误导致, 不能用栈结构模拟出遍历的过程, 在非递归后序遍历,没用弄清楚循环判断和条件判断,导致在遍历过程中出现错误,最后采用得到方式就是判断栈顶元素与当前节点的父节点是否是同一个,通过这样的方式判断是否输出该节点的父节点内容。

树结构体:

struct BitNode{BitNode(char c,BitNode *p):data(c),parent(p){}Type data;BitNode *parent=NULL,*lchild=NULL,*rchild=NULL; 
};

非递归先序遍历:

void nrperOrder(BitNode *bt){//非递归先序遍历int top=-1;BitNode *stack[size],*p;if(bt!=NULL)stack[++top]=bt;else return;while(top>-1){p=stack[top--];cout<<p->data;//有右存右,有左存左,先存右再存左 if(p->rchild!=NULL)stack[++top]=p->rchild;if(p->lchild!=NULL)stack[++top]=p->lchild;}
}

非递归中序遍历:

void nrinOrder(BitNode *bt){//非递归中序遍历int top=-1;BitNode *stack[size],*p=bt;while(p||top>-1){if(p){stack[++top]=p;p=p->lchild;}else{p=stack[top--];cout<<p->data;p=p->rchild;}}
}

非递归后序遍历:

void nrpastOrder(BitNode *bt){//非递归后序遍历int top=-1,tag[size];BitNode *stack[size],*p=bt;stack[++top]=bt;
while(top>-1){//左右节点都访问过才能访问parent//当栈顶元素与当前元素p的parent是一个,则说明p存在兄弟节点还没被访问 if(stack[top]!=p->parent){p=stack[top];while(p){if(p->rchild)stack[++top]=p->rchild;if(p->lchild)stack[++top]=p->lchild;if(p->lchild)p=p->lchild;//尽量往左偏移 else if(p->rchild)p=p->rchild;//做不下去,往右走 else p=NULL;//左右都走不下去,p置空 }}p=stack[top--];cout<<p->data;}
}

求根节点到指定节点的路径:

void path(BitNode *bt,char c,BitNode *parent){//方法1.先序遍历,找到目标节点,不断返回父节点(即当前节点到根节点路径),中后序遍历一致 //方法2.迭代式查找,找到当前节点然后迭代输出parent int top=-1;BitNode *stack[size],*p;if(bt!=NULL)stack[++top]=bt;else return;while(top>-1){p=stack[top--];if(p->data==c){while(p){cout<<p->data;p=p->parent;}return;}if(p->rchild!=NULL)stack[++top]=p->rchild;if(p->lchild!=NULL)stack[++top]=p->lchild;}
}

完整函数调用代码如下:

#include<iostream>
#define Type char
#define size 20
using namespace std;
struct BitNode{BitNode(char c,BitNode *p):data(c),parent(p){}Type data;BitNode *parent=NULL,*lchild=NULL,*rchild=NULL; 
};
void createBtree(BitNode* &bt,BitNode* parent){//创建二叉树char c;if((cin>>c)&&(c!='#')&&(c!='\n')){bt=new BitNode(c,parent);parent=bt;createBtree(bt->lchild,parent);createBtree(bt->rchild,parent);}
}
void nrperOrder(BitNode *bt){//非递归先序遍历int top=-1;BitNode *stack[size],*p;if(bt!=NULL)stack[++top]=bt;else return;while(top>-1){p=stack[top--];cout<<p->data;//有右存右,有左存左,先存右再存左 if(p->rchild!=NULL)stack[++top]=p->rchild;if(p->lchild!=NULL)stack[++top]=p->lchild;}
}
void nrinOrder(BitNode *bt){//非递归中序遍历int top=-1;BitNode *stack[size],*p=bt;while(p||top>-1){if(p){stack[++top]=p;p=p->lchild;}else{p=stack[top--];cout<<p->data;p=p->rchild;}}
}
void nrpastOrder(BitNode *bt){//非递归后序遍历int top=-1,tag[size];BitNode *stack[size],*p=bt;stack[++top]=bt;
while(top>-1){//左右节点都访问过才能访问parent//当栈顶元素与当前元素p的parent是一个,则说明p存在兄弟节点还没被访问 if(stack[top]!=p->parent){p=stack[top];while(p){if(p->rchild)stack[++top]=p->rchild;if(p->lchild)stack[++top]=p->lchild;if(p->lchild)p=p->lchild;//尽量往左偏移 else if(p->rchild)p=p->rchild;//做不下去,往右走 else p=NULL;//左右都走不下去,p置空 }}p=stack[top--];cout<<p->data;}
}void path(BitNode *bt,char c,BitNode *parent){//方法1.先序遍历,找到目标节点,不断返回父节点(即当前节点到根节点路径),中后序遍历一致 //方法2.迭代式查找,找到当前节点然后迭代输出parent int top=-1;BitNode *stack[size],*p;if(bt!=NULL)stack[++top]=bt;else return;while(top>-1){p=stack[top--];if(p->data==c){while(p){cout<<p->data;p=p->parent;}return;}if(p->rchild!=NULL)stack[++top]=p->rchild;if(p->lchild!=NULL)stack[++top]=p->lchild;}
}void removeBtree(BitNode *bt){if(bt){removeBtree(bt->lchild);removeBtree(bt->rchild);delete bt;}
}
int main(){//ABD#GJ#K###EH###C#F#I##BitNode *bt;char c;int count=0; cout<<"创建二叉树:";createBtree(bt,NULL);cout<<"非递归前序遍历:";nrperOrder(bt);cout<<endl; cout<<"非递归中序遍历";nrinOrder(bt);cout<<endl;cout<<"非递归后序遍历:";nrpastOrder(bt);cout<<endl;cout<<"树高:"<<btreeHeight(bt);cout<<endl;cout<<"路径,输入查找字符:";cin>>c; path(bt,c,NULL);cout<<endl;removeBtree(bt);//删除二叉树return 0;
}

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

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

相关文章

nedc工况_东南DX3 EV续航升级 NEDC综合工况续航451公里

东南DX3 EV续航升级版车型曝光&#xff0c;电池系统能量密度由之前的141Wh/kg提升至了161Wh/kg&#xff0c;NEDC综合工况续航也由老款产品的351km提升至了451km。近日&#xff0c;工信部公布了2019年第7批《新能源汽车推广应用推荐车型目录》&#xff0c;东南DX3 EV续航升级版车…

不笑找我系列 | 程序员爆笑漫画十条

原创翻译~ 转载请说明出处~~~~~~~~ 1、如果你让码农给你做个事情&#xff0c;比如修个灯泡&#xff0c;他会这样去执行你的指令 2、分享一个码农发现并解决bug的过程&#xff0c;实在是符合我们码农的人设 3、码农的一天&#xff0c;像极了我的一天 4、至今为止&#xff0c;没…

AVL树学习总结

AVL树 平衡二叉树的缺点 由于平衡二叉搜索树的search(), insert(),remove()接口的运行时间与二叉树的高度成正比,所以若不能有效控制树高, 从平均复杂度来看,二叉平衡搜索树并不能让人满意 理想平衡 二叉树的性能取决于树的高度,只有当左右子树的高度接近时才能达到理想平衡…

nginx编译安装_Nginx编译安装nginx-upsync-module模块以实现动态负载

安装依赖包OpenSSL在官网下载页下到最新稳定版1.0.2q。PCRE在 PCRE 官网可以找到下载地址&#xff0c;这里选择8.x的最高版本 pcre-8.42.tar.gz。zlibzlib 直接选择官网首页最新的zlib-1.2.11.tar.gz。下载nginx 源码包及nginx-upsync-module模块源码这里下载的是nginx稳定版ng…

码农笑话图片十张

原创翻译~转载请说明出处~~~~~~~~1、如果你让码农给你做个事情&#xff0c;比如修个灯泡&#xff0c;他会这样去执行你的指令2、分享一个码农发现并解决bug的过程&#xff0c;实在是符合我们码农的人设3、码农的一天&#xff0c;像极了我的一天4、至今为止&#xff0c;没有遇到…

伸展树学习总结

伸展树 与AVL树类似, 伸展树也是二叉搜索树的一种形式, 伸展树无需保证时刻保持全树的平衡,也不需要像AVL树一样要求记录平衡因子的附加信息 伸展树的提出源于信息访问的局部性(刚被访问过的信息有可能再次被访问,要被访问的元素可能位于刚访问过的元素的附近), 就伸展树而言…

c语言 库打印函数

函数#include<stdio.h>int printf(const char *format, ... );/* [until c99]写结果到stdout */int printf(const char *restrict format, ... );/* [since c99] */int fprintf(FILE *stream, const char *format, ... );/* [until c99]写结果到文件流stream */int fprin…

xhprof windows下安装和使用(转载)

1、使用5.3.3以上的php版本&#xff0c;或者直接下载wamp2.1集成环境。 2、下载xhprof for windows版本&#xff0c;地址&#xff1a;http://www.benjamin-carl.de/?downloadXHProf-0.10.0-PHP-5.3.3-VC6-x86-TS&#xff0c;把解压后的dll文件放到php的ext目录里面。 关于这个…

B树学习总结

B树 多路搜索树 当数据规模足够大的时候, 大部分数据存储到外存中, 常规的平衡二叉搜索树将无法满足需求理由如下: 常规二叉平衡搜索树每当进行search(),insert(),remove()操作的时候就会进行节点的切换,从而导致大量的IO操作,就此提出了多路搜索,尝试将原本的多个节点合在一…

嵌入式未来趋势是什么?

感谢CSDN编辑邀请 前几天通过了CSDN博客专家的申请&#xff0c;在CSDN博客摸爬滚打多少个日日夜夜后终于修成正果了&#xff0c;当时通过CSDN博客专家时赶紧把消息发给了以前的创新基地同学&#xff0c;想起来&#xff0c;我们那时候刚开始学习单片机&#xff0c;就是从CSDN上面…

js动态添加删除节点

转载于:https://www.cnblogs.com/jiaobaobao/p/6762692.html

书中自有黄金屋~外加中奖结果通知

人生的路很长&#xff0c;很艰辛&#xff0c;只有不断学习才能超越梦想&#xff0c;大牛是如何成长的&#xff01;首先要多看多听多写~~今天推荐一些大牛们都关注的技术公众号&#xff0c;机器学习、架构、前端、嵌入式、PY学习.....希望能够帮助到大家&#xff0c;引领我们前行…

拨号云服务器怎么自动配置网关_云服务器配置网站卡慢怎么办

网站访问卡慢有很多原因&#xff0c;一次完整的 HTTP 请求包括域名解析、建立 TCP 连接、发起请求、云服务器接收到请求进行处理并返回处理结果、浏览器对 HTML 代码进行解析并请求其他资源&#xff0c;以及对页面进行渲染呈现。其中&#xff0c;HTTP 的请求过程经历了用户本地…

手机java软件_浅谈软件开发就业前景

​  我国信息化人才培养还处于发展阶段&#xff0c;导致社会实际需求人才基数远远大于信息化人才的培养基数&#xff0c;使得数以万计的中小企业急需全面系统掌握软件开发基础技能与知识的软件工程师。目前对软件已达20万并且以每年20%左右的速度增长。在未来5年内合格软件人…

ufldl学习笔记与编程作业:Multi-Layer Neural Network(多层神经网络+识别手写体编程)...

ufldl学习笔记与编程作业&#xff1a;Multi-Layer Neural Network(多层神经网络识别手写体编程) ufldl出了新教程&#xff0c;感觉比之前的好&#xff0c;从基础讲起&#xff0c;系统清晰&#xff0c;又有编程实践。 在deep learning高质量群里面听一些前辈说&#xff0c;不必深…

sql查询无结果返回空_3分钟短文 | Laravel 查询结果检查是不是空,5个方法你别用错...

引言Laravel 提供了 Eloquent ORM 对象用于操作数据库&#xff0c;将其进行抽象方便操作。因为设计的灵活度&#xff0c;大家在使用Model查询数据集的时候&#xff0c;会面临结果为空&#xff0c;记录不存在的问题&#xff0c; 那么如何有效地判断查询记录为空呢&#xff1f;本…

深度优先搜索小结

深度优先搜索(DFS) 深度优先搜索就好比走迷宫, 不断顺着一条路走, 直到走不通为止, 然后回退到上一个路口再向另外的方向行走(走过的方向就不会再走了,又不是傻子, 知道走不通,还向走不通的方向走), 不断重复(试过所有路口, 状态转移), 重复直到找到唯一的一条合适的路径; DFS…

python 串口_如何使用Python开发串口通讯上位机(一)

用Python开发串口通讯型上位机&#xff0c;其实并非最优解&#xff0c;本系列更新只为个人学习与总结。如果有C语言底子&#xff0c;嵌入式层面的上位机开发&#xff0c;C Builder&#xff0c;C#才是更加好用的利器。1什么是上位机从事过嵌入式软件开发或者工控机开发的&#x…

算法题之求二叉树的最大距离

二叉树是一种非常经典的数据结构。如果我们把二叉树看成一个图&#xff0c;父子节点之间的连线看成是双向的&#xff0c;我们姑且定义"距离"为两节点之间边的个数。写一个程序求一棵二叉树中相距最远的两个节点之间的距离。 下面我们随意构造出一棵二叉树&#xff0c…

php 公众号验证回调方法_微信公众号关键词自动回复设置方法!

什么是公众号关键词自动回复&#xff1f;在微信公众号平台设置关键词自动回复&#xff0c;可以通过添加规则&#xff0c;关注/订阅的用户发送的消息内容如果是你设置的关键字&#xff0c;即可以实现自动回复预先设置好的内容。关键字自动回复设置方法&#xff1a;1、 首先我们进…