一.非递归先序遍历
1.思路分析
a.使用while循环从树根节点开始用指针p遍历二叉树
b.如果遍历到的节点p不是空树,则访问该节点,并把节点p入栈(入栈是因为回溯访问其右子树需要通过其获得它的右子树),然后p指向p的左子树(向左走)
c.如果遍历到空树(说明向左走到了尽头),则出栈一个节点,p指向该节点的右子树
(向右走)
d.就这样一直循环直到栈为空且p指针指向空,则先序遍历二叉树完成
2.代码实现
//先序遍历二叉树(非递归)
void PreOrderTraverse(BiTree T,void (*visit)(TElemType *elem))
{
if(T==NULL) //如果空树退出函数
return;
BiTree stack[500]; //用数组创建一个栈
int top=0; //栈顶
int base=0; //栈底
BiTree p=T; //指向树节点的指针
while(p!=NULL||top!=base) //p为空同时栈为空时结束循环
{
if(p!=NULL) //向左不为空
{ //这些操作是向左走到尽头
visit(&p->data); //先序访问节点
stack[top++]=p; //节点入栈
p=p->lchild; //向左子树走
}
else //节点为空,说明向左走到了尽头,准备出栈
{
p=stack[--top]; //节点出栈
p=p->rchild; //向该节点的右子树走
}
}
}
二.非递归中序遍历
1.思路分析
中序和先序的非递归遍历算法是类似的,只不过中序是在节点出栈时才对节点进行访问的
a.使用while循环从树根节点开始用指针p遍历二叉树
b.如果遍历到的节点p不是空树,则把节点p入栈(入栈是因为要对其回溯访问,而且需要通过其获得它的右子树),然后p指向p的左子树(向左走)
c.如果遍历到空树(说明向左走到了尽头),则出栈一个节点并访问该节点,然后p指向该节点的右子树(向右走)
d.就这样一直循环直到栈为空且p指针指向空,则中序遍历二叉树完成
2.代码实现
//中序遍历二叉树
void InOrderTraverse3(BiTree T,void (*visit)(TElemType *elem))
{
if(T==NULL) //如果空树退出函数
return;
BiTree stack[500]; //用数组创建一个栈
int top=0; //栈顶
int base=0; //栈底
BiTree p=T; //指向树节点的指针
while(p!=NULL||top!=base) //p为空同时栈为空时结束循环
{
if(p!=NULL) //向左不为空
{ //这些操作是向左走到尽头
stack[top++]=p; //节点入栈
p=p->lchild; //向左子树走
}
else //节点为空,说明向左走到了尽头,准备出栈
{
p=stack[--top]; //节点出栈
visit(&p->data); //中序访问节点
p=p->rchild; //向该节点的右子树走
}
}
}
三.非递归后序遍历
1.思路分析
类似于先序和后序遍历的非递归算法,非递归后序遍历算法也是需要用到辅助栈,然后进行节点回溯,但非递归后序遍历还需要一个标记指针pre来标记节点的右子树是否被访问过,因为后序遍历是左右子树被访问过才能访问根节点
a.使用while循环从树根节点开始用指针p遍历二叉树
b.如果遍历到的节点p不是空树,则把节点p入栈(入栈是因为要对其回溯访问,而且需要通过其获得它的右子树),然后p指向p的左子树(向左走)
c.如果遍历到空树(说明向左走到了尽头),p获取栈顶元素,不出栈,因为可能其右子树还没有被访问,然后有两种情况
①如果栈顶节点p存在右子树且未被访问(p->rchild!=pre),则p指向栈顶节点的右子树(向右走),并且将该右子树入栈,然后p指向右子树节点的左子树(向左走)
②如果栈顶节点不存在右子树或者其右子树为已被访问,则出栈一个节点对其进行访问,并把标记指针pre指向节点(标记该节点已被访问),然后p赋为空(为了回溯到节点的根节点).
d.就这样一直循环直到栈为空且p指针指向空,则后序遍历二叉树完成
2.代码实现
//后序遍历二叉树(非递归)
void PostOrderTraverse(BiTree T,void (*visit)(TElemType *elem))
{
if(T==NULL) //如果是空树退出函数
return;
BiTree stack[500]; //用数组构建一个栈
int top=0; //栈顶
int base=0; //栈底
BiTree p=T; //指向树节点的指针
BiTree pre=NULL; //用于标记前一访问位置的指针
while(p!=NULL||top!=base) //当p为空且栈为空时退出循环
{
if(p!=NULL) //树非空
{ //这些操作是向左走到尽头
stack[top++]=p; //左子树入栈
p=p->lchild; //向左走
}
else //节点为空,说明向左走到了尽头,准备出栈
{
p=stack[top-1]; //先读取栈顶元素,但是不出栈,因为可能其右子树还没有被访问
if(p->rchild!=NULL&&p->rchild!=pre) //如果有右子树,且右子树没有被访问
{
p=p->rchild; //向右走
stack[top++]=p; //右子树入栈
p=p->lchild; //遍历右子树的左子树
}
else //没有右子树或者右子树已经被访问
{
p=stack[--top]; //根节点出栈
visit(&p->data); //后续访问根节点
pre=p; //标记节点已被访问
p=NULL; //用于回到根节点
}
}
}
}
四.层序遍历
1.思路分析
a.层序遍历需要一个辅助队列,先把树根节点T入队
b.然后进行while循环
第一步:进行出队,访问出队节点p
第二步:如果出队节点p的左子树存在,则使其左子树入队
第三步:如果出队节点p的右子树存在,则使其右子树入队
c.就这样循环直到队列为空,则层序遍历二叉树完成
2.代码实现
//层序遍历二叉树(非递归)
void LevelOrderTraverse(BiTree T,void (*visit)(TElemType *elem))
{
if(T==NULL) //如果空树退出函数
return;
BiTree queue[500]; //用数组创建一个队列
int front=0; //队头
int rear=0; //队尾
queue[rear++]=T; //根节点入队
BiTree p; //遍历树节点的指针
while(front!=rear)
{
p=queue[front++]; //出队
visit(&p->data); //遍历根节点
if(p->lchild!=NULL) //左孩子非空入队
queue[rear++]=p->lchild;
if(p->rchild!=NULL) //右孩子非空入队
queue[rear++]=p->rchild;
}
}