树的四种遍历(先序、中序、后序、层次)
一.树的先序遍历(迭代)
- 首先我们要给出树的结点类,提供所有遍历通用的接口:
#define BinNodePosi(T) BinNode<T>* //结点位置
template<typename T>
struct BinNode{
BinNodePosi(T) parent, lChild, rChild; //父亲和孩子结点
T data; //数据
int height; //高度
int size(); //子树规模
BinNodePosi(T) insertAsLC(T const& );//作为左孩子插入新结点
BinNodePosi(T) insertAsRL(T const& );//作为右孩子插入新结点
BinNodePosi(T) succ(); //当前结点的直接后继(中序遍历意义下)
template<typename VST> void travLevel(VST& );//子树层次遍历
template<typename VST> void travPre(VST& );//子树先序遍历
template<typename VST> void travIn(VST& );//子树中序遍历
template<typename VST> void travPost(VST& );//子树层次遍历
};
- visit例程:
template<typename T, typename VST>
static void visitAlongLeftBranch(BinNodePosi(T) x, VST& visit, Stack<BinNodePosi(T)>& s){
while(x){
visit(x->data);//访问当前结点
S.push(x->rChild);//右孩子入栈(将来逆序出栈)
x = x->lChild;//沿左侧链下行
}//只有右孩子,NULL可能入栈—增加判断剔除后者,不值得
}
- 主算法:
template<typename T, typename VST>
void travPre(BinNodePosi(T) x, VST& visit){
Stack<BinNodePosi(T)> S;//辅助栈
while (true){//以右子树为单位,逐批访问结点
visitAlongLeftBranch(x, visit, S);//访问子树x的左侧链,右子树入栈缓存
if(S.empty()) break;
x = S.pop();//弹出下一子树的根
}
}
由于树的先序遍历的递归写法为两句尾递归,所以很容易将之转换为迭代形式。
二.树的中序遍历(迭代)
- go例程:
template<typename T>
static void goAlongLeftBranch(BinNodePosi(T) x, Stack<BinNodePosi(T)>& S){
while(x){
S.push(x);//左孩子反复入栈,沿左分支深入
x = x->lChild;
}
}
- 主算法:
template<typename T, typename V>
void travIn(BinNodePosi(T) x, V& visit){
Stack<BinNodePosi(T)> S;//辅助栈
while(true){
goAlongLeftBranch(x, S);//从当前结点出发,逐批入栈
if(S.empty()) break;
x = S.pop();//X的左子树或为空(或已遍历),故
visit(x->data);//立即访问它
x = x->rChild;//再转向其右子树(可能为空)
}
}
迭代的分摊分析复杂度为O(n),但常系数要比递归小的多。
三.树的后序遍历(迭代)
- goto例程:
template<typename T>
static void gotoLeftmostLeaf(Stack<BinNodePosi(T)>& S){
while(BinNodePosi(T) x = S.top())//自顶而下反复检查栈顶结点
if(HasLChild(*x)){//HasLChild判断是否有左孩子,尽可能向左,在此之前,
if(HasRChild(*x))//若有右孩子,则
S.push(x->rChild);//优先入栈(先进后出)
S.push(x->lChild);//然后转向左孩子
}else //实不得已
S.push(x->rChild);//才转向右孩子
S.pop;//返回之前,弹出栈顶的空结点
}
- 主算法:
template<typename T, typename VST>
void travPost(BinNodePosi(T) x, VST& visit){
Stack<BinNodePosi(T)> S;//辅助栈
if(x) S.push(x);//根结点首先入栈
while(!S.empty()){//x始终为当前结点
if(S.top() != x->parent)//若栈顶非x之父(而为右兄弟)
gotoLeftmostLeaf(S);//则在其右兄子树中找到最靠左的叶子—无孩子的结点(递归深入)
x = S.pop();//弹出栈顶(即前一结点之后继)以更新x
visit(x->data);//并随之访问
}
}
四.树的层次遍历(迭代)
- 主算法:
template<typename T, typename VST>
void travLevel(VST& visit){
Queue<BinNodePosi(T)> Q;//辅助队列
Q.enqueue(this);//根结点入队
while(!Q.empty()){
BinNodePosi(T) x = Q.dequeue();//取出队首结点,并随即
visit(x->data);//访问之
if(HasLChild(*x)) Q.enqueue(x->lChild);//左孩子入队
if(HasRChild(*x)) Q.enqueue(x->rChild);//右孩子入队
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了