- 我的上篇博客给出了二叉树的创建和前中后序遍历算法,但它们都是基于递归实现的,虽然代码简洁,但理解起来有点费劲,想当初第一次看到这么简洁的遍历算法后,心里连连跳出三个卧槽,实在是简洁的不像话,这里先给出我自己对递归的理解.
- 递归像剥洋葱,
假设我们剥了一层皮后还要洗干净这层皮,这一步才算完成
,那么我们在剥完一次皮后,本来按步骤要去洗的,但剥完发现还有一层皮要剥,于是我们就先不洗了,而是立马把新出现的皮给剥了,之后同样看到皮就一路剥下去,每层皮洗的动作都先延后,直到剥完最后一层皮,发现没皮可剥了,那我们就把最后一层皮拿去洗,这样我们对最后一层皮的处理就彻底完成了,完成后这层皮后出现再你手里的就是倒数第二层皮
,然后你那它去洗,则第二层皮的处理就完成了,紧接着去洗倒数第三层…依次类推,直到洗好第一层皮那么整个动作就完成了. - 前中后序的非递归遍历算法都是依靠
辅助栈
来实现的,而层次遍历
是通过循环队列
实现的,下面依次给出操作原理 - 前序遍历(
DLR
)的非递归思想:
- 先定义一个栈,把二叉树的根结点入栈
- 然后栈顶出栈,一出栈便输出出栈结点,出栈的同时判断它是否有左右孩子,要有则入栈.
注:要是左右孩子都存在则先右孩子入栈,因为先进后出嘛
- 最后在栈不为空的条件下一直循环执行步骤2
- 中序遍历(
LDR
)的非递归思想
- 先定义一个栈,把二叉树的根结点入栈
- 把从根结点出发的左孩子一路入栈
- 碰到第一个没有左孩子的结点则访问输出该结点(即栈顶出栈结点),并让它指向自己的右孩子
- 若上一步中该结点有右孩子,则以他的右孩子出发,它有左孩子则左孩子统统入栈,即重复步骤2,步骤3
- 若步骤3中的结点没有右孩子,只要栈不为空便栈顶出栈,接着继续做步骤3的判断
- 后序遍历(
LRD
)的非递归遍历思想
- 和前序遍历的过程一样就四点不同的地方,其他都一样
- 需要两个辅助栈实现
- 当栈1出栈顶点左右孩子都存在时,先左孩子入栈
- 栈1出栈结点立马压入栈2,并不像前序那样立马访问输出
- 最后统一让栈2一次性出栈,每个结点一出栈就访问输出
- 后序遍历能这么干的依据是:
逆后序遍历就是把先序遍历中对左右子树的遍历顺序交换的结果
- 和前序遍历的过程一样就四点不同的地方,其他都一样
二叉树的层次遍历实现思想(
和图的广度优先遍历思想一致
)- 先创建一个二叉树结点类型队列,根结点若不为空则入队
- 紧接着对头出队,马上访问输出出队结点的数据域,出队的同时判断该结点是否有左右孩子,有的话则统统入队,
注:左右孩子都有时先左孩子入队,先进先出嘛
- 在队不为空的情况下循环执行以上两步
好了,上代码才是王道
#include <iostream>
#include <stdlib.h>
#define maxSize 100
typedef struct BTNode{
char data;
struct BTNode *lchild;
struct BTNode *rchild;
}BTNode;
void createBTree(BTNode *&p){//前序遍历创建二叉树,所以创建完成后p肯定就是二叉树的根结点
char ch;
scanf("%c",&ch);
if(ch=='#'){
p=NULL;
}else{
p=(BTNode *)malloc(sizeof(BTNode));
p->data=ch;
createBTree(p->lchild);
createBTree(p->rchild);
}
}
void preOrderSt(BTNode *p){//前序遍历的非递归实现
BTNode *stack[maxSize];
int top;
top=-1;
BTNode *q;
if(p!=NULL){
stack[++top]=p;//根结点入栈
while(top!=-1){
q=stack[top--];//根结点出栈
printf("%c ",q->data);//访问
if(q->rchild!=NULL){//若结点有右孩子则右孩子入栈(先进后出嘛)
stack[++top]=q->rchild;
}
if(q->lchild!=NULL){//有左孩子则左孩子入栈
stack[++top]=q->lchild;
}
}
}
}
void inOrderSt(BTNode *p){//中遍历的非递归实现
BTNode *stack[maxSize];
BTNode *q;
int top;
top=-1;
while(p!=NULL||top!=-1){
while(p!=NULL){//左孩子存在的话一直进栈
stack[++top]=p;
p=p->lchild;
}
if(top!=-1){//左孩子不存在则一路出栈,出栈的同时得判读它有没右孩子
p=stack[top--];//没有左孩子了,则栈顶出栈
printf("%c ",p->data);//一出栈便立马访问
p=p->rchild;//若右孩子存在,会进入上面的while循环,再度判读其右孩子有没有左孩子
}
}
}
void postOrderSt(BTNode *p){//后序遍历的非递归实现
BTNode *stack1[maxSize];int top1=-1;
BTNode *stack2[maxSize];int top2=-1;
BTNode *q,*s;
stack1[++top1]=p;//入栈
while(top1!=-1){//若栈不为空
q=stack1[top1--];//根结点出栈的同时看看有没有左右孩子,有就进栈(先左后右)
stack2[++top2]=q;//栈1出栈的元素栈2马上入栈
if(q->lchild!=NULL){
stack1[++top1]=q->lchild;
}
if(q->rchild!=NULL){
stack1[++top1]=q->rchild;
}
}
while(top2!=-1){//栈2中出栈输出
s=stack2[top2--];
printf("%c ",s->data);
}
}
void level(BTNode *p){//二叉树的层次遍历
BTNode *bt[maxSize];
BTNode *q;
int front,rear;//建立循环队列
front=rear=0;//初始化队列
if(p!=NULL){
rear=(rear+1)%maxSize;
bt[rear]=p;//根结点入队
printf("层序遍历序列为: ");
while(front!=rear){//队不为空时循环
front=(front+1)%maxSize;
q=bt[front];//头结点出队
printf("%c ",q->data);//出队就打印
if(q->lchild!=NULL){//若出队结点有左孩子则左孩子入队
rear=(rear+1)%maxSize;
bt[rear]=q->lchild;
}
if(q->rchild!=NULL){//若出队结点有右孩子则右孩子入队
rear=(rear+1)%maxSize;
bt[rear]=q->rchild;
}
}
}
}
int main(int argc, char** argv) {
BTNode *p;
createBTree(p);
printf("前序非递归遍历序列: ");
preOrderSt(p);
printf("\n");
printf("中序非递归遍历序列: ");
inOrderSt(p);
printf("\n");
printf("后序非递归遍历序列: ");
postOrderSt(p);
printf("\n");
printf("层次遍历序列: ");
level(p);
printf("\n");
return 0;
}