之前的文章,我们详细的讲述了二叉树的创建、遍历等的实现,但是都是以递归的方式。其实我们知道递归的过程是一个不断开辟栈帧的过程,会影响算法的执行效率,那么这篇文章主要是站在C语言的角度为大家分析一下二叉树的非递归前序、中序、后序遍历~
1、二叉树的前序遍历
(1)第一步:建立一个二维指针,用来存储每一个结点的地址,定义栈顶指针top,初始化为-1,并将根结点存入栈中
(2)第二步:栈顶指针不为-1的时候就进入while循环,输出当前栈顶元素p的数据域,代表前序遍历的第一个结点为根结点
(3)第三步:如果当前的p结点拥有右子树,将这个右子树结点存入栈中,没有则不存(因为栈是先进后出,所以先存右子树,再存左子树)
(4)第四步:如果当前的p结点拥有左子树,将这个左子树结点存入栈中,没有则不存
(5)再判断top是否为-1,之后重复执行2,3,4步骤。
void PreOrder(BitTree *bt)
{
BitTree **s;
BitTree *p;
int top = -1;
//创建栈
s = (BitTree **) malloc((N+1)*sizeof(BitTree*));
//初始化栈
s[++top] = bt;
//非递归前序遍历
while(top != -1)
{
p = s[top--];
printf("%c",p->ch);
if(p->rchild)
s[++top]=p->rchild;
if(p->lchild)
s[++top]=p->lchild;
}
free(s);
}
2、二叉树的中序遍历
(1)第一步:与非递归前序遍历相同,同样需要一个二维指针的栈,栈顶元素初始化为-1;
(2)首先if的判断条件,这个二叉树不为空
(3)再进入if条件语句中的while循环,这个循环是将根结点相连所有左子树全部存入栈中
(4)退出循环过后,再判断第二个while循环的条件,栈不为空,则进入while循环
(5)首先输出距离与根结点相连最远的左子树
(6)如果这个结点拥有右子树,先将这个右子树结点存入栈中
(7)再判断这个结点是否拥有左子树,有的话将与这个结点相连的所有左子树存入栈中,没有就不执行while循环
(8)在判断top是否为-1,成立进入循环,重复上述步骤即可;
void InOrder(BitTree *bt)
{
BitTree **s;
BitTree*p,*q;
int top = -1;
s = (BitTree **) malloc((N+1)*sizeof(BitTree*));//创建栈
if(bt)
{
while(bt)//一直遍历左子树知道该结点的左子树空为止
{
s[++top] = bt;//将所有左孩子存入栈中
bt =bt->lchild;//指向下一个左子树
}
while(top != 1)//栈空时结束循环
{
p = s[top--];//刚开始将p指向左下角的左孩子,并且移向该结点的父结点
printf("%c",p->ch);//输出左下角的结点
while(p->rchild)//遍历移动后结点有没有右结点
{
s[++top] = p->rcihld;//将这个结点的右子树入栈
q = p->rchild;//这个右子树结点赋值给q
while(q->lchild)//判断结点q有没有左子树
{
s[++top] = q->lchild;//有左子树,将与这个结点相连的所有左子树都入栈
q = q->lchild;
}
break;//结束当前循环,回到第二个while循环继续刚才步骤
}
}
}
}
3、二叉树的后序遍历
1> 同样建立一个二维指针的栈,用来存储每个结点的地址;
2> 进入do-while循环,首先,将与根结点相连的所有左子树都存入栈中;
3> 将BitTree类型的指针p赋空,判断while循环是否成立;
4> 如果这个最后一个结点的右子树==p,则输出当前结点的数据域,栈顶指针–;
5> 并将这个结点赋给指针p表示这个结点已经访问过,并且已经输出;
6> 如果当前结点的右子树!=p,则接着访问这个结点的右子树,并且结束while循环,判断do-while循环的判断条件;
7> 重复上述过程,知道不满足do-while循环的判断条件;
void PostOrder(BitTree* bt)
{
BitTree **s;
BitTree *p;
int top = -1;
s = (BitTree **) malloc((N+1)*sizeof(BitTree*));
do
{
while(bt)//一直遍历左子树直到该左子树的左孩子为空为止
{
s[++top] = bt;//将所有左孩子存入栈中
bt = bt->lchild;//指向下一个左结点
}
p = NULL;
while(top != -1)
{
bt = s[top];
if(bt->rchild == p)//p表示为空或者右子节点被访问过了
{
printf("%c",bt->ch);
top --;
p = bt;//p记录下刚刚访问的结点
}
else
{
bt = bt->rchild;//访问右子树结点
break;
}
}
}while(top != 1;
}