伪代码阐述

先序遍历

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

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;
}

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

  1. 二叉树前序遍历python输出_[宜配屋]听图阁 - Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例...

    本文实例讲述了Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作.分享给大家供大家参考,具体如下: 实现一个功能: 输入:一颗二叉树的先序和中序遍历 输出:后续遍历 思想: 先序遍历中,第 ...

  2. 为什么普通树没有中序遍历和森林没有后序遍历

    本文为个人理解,若有错误欢迎指正! 这里说的树当然是指普通的树,而不是树中比较特别的二叉树. 1.为什么普通树没有中序遍历 中序遍历是对于二叉树而言: 中序遍历左子树,访问根结点,中序遍历右结点. 在 ...

  3. 二叉树路径应用举例(基于非递归后序遍历)

    #include "stdafx.h" #include <iostream> #include <fstream>using namespace std; ...

  4. matlab深度遍历程序,Matlab对文件夹的层次遍历和深度遍历

    最近做一个项目,由于数据分别放在不同的文件夹中,对大量数据文件"打开->复制->粘贴",觉得很费事,于是就写了对基于Matlab的文件夹遍历.文价夹遍历有两种方式,即层 ...

  5. 给定二叉树先序、中序遍历序列,求后序遍历

    给定一个二叉树的前序遍历和中序遍历的序列,输出对应这个二叉树的后续遍历序列. 输入描述: 输入为一行. 两个字符串,分别表示二叉树的前序遍历和中序遍历结果,用空格分隔.保证数据合法 输出描述: 对应输 ...

  6. 二叉树前序中序后序_leetcode889_go_根据前序和后序遍历构造二叉树

    leetcode889_根据前序和后序遍历构造二叉树 01 - 题目 返回与给定的前序和后序遍历匹配的任何二叉树. pre 和 post 遍历中的值是不同的正整数. 示例:输入:pre = [1,2, ...

  7. 已知二叉树先序和中序遍历结果,求后序遍历结果

    以下面的例题为例进行讲解:已知一棵二叉树的先序遍历序列和中序遍历序列分别是ABDCEF.BDAECF,求二叉树及后序遍历序列. 分析:先序遍历序列的第一个字符为根结点.对于中序遍历,根结点在中序遍历序 ...

  8. leetcode 236. 二叉树的最近公共祖先LCA(后序遍历,回溯)

    LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先. 题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先.百度百科 ...

  9. 二叉树前序中序后续线索树_后序线索二叉树怎么画 线索二叉树基本操作详解 - 办公软件 - 服务器之家...

    后序线索二叉树怎么画 线索二叉树基本操作详解 发布时间:2017-05-23 来源:服务器之家 遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序,中序或后序序列.这实际上 ...

最新文章

  1. “深度学习之父”后悔投少了?90后华人学者:我们只卖AI软件
  2. CTF的一道安卓逆向
  3. swift 如何实现点击view后显示灰色背景
  4. 使用渲染纹理的制作摄像头
  5. 阿里云帮助江苏省财政厅力推统一公共支付平台
  6. 有趣的灵魂,从高质量的阅读开始
  7. 给element的select添加复选框
  8. Method-Swizzling实战-实现iOS原生网络请求性能采集
  9. html设计动画小黄人,CSS3实现可爱的小黄人动画
  10. ThinkPHP 使用极光推送给ios推送消息
  11. 基于Matlab----RSSI指纹定位技术性能仿真
  12. HDU 4335 What is N?(指数循环节)题解
  13. python去除\ufeff、\xa0、\u3000
  14. 【linux就该这么学】-09
  15. 钓鱼网站制作Kali
  16. 315/433MHZ无线遥控接收解码源程序 Keil源程序 含AD格式电路图
  17. linux sox录音时间控制,SOX的一些命令和kaldi使用sox音频数据增强
  18. Node之使用os模块获取操作系统信息
  19. vba excel 开发游戏_自动化神器—VBA
  20. [31期] 31期战地日记

热门文章

  1. 12-- 缺失的第一个正数
  2. c语言贪吃蛇最简单代码_C语言指针,这可能是史上最干最全的讲解啦(附代码)!!!...
  3. html股票数据代码,股票数据的网站抓取(4.2)代码优化
  4. bem什么意思_bem是什么意思_bem的翻译_音标_读音_用法_例句_爱词霸在线词典
  5. 应用层协议:HTTP与HTTPS协议详解、二者的区别
  6. 网站服务器中病毒该如何处理,网站被中了木马无法删除怎么办? 解决网站中病毒的办法...
  7. unity改变图片像素大小_类动森像素画反向转换 (xBR)
  8. 洛谷 P1272 重建道路
  9. java-构建jar带哟参数提示的
  10. json字符串与json对象之间的转换