在上一篇文章中完成了二叉树的递归版本的先序、中序、后序遍历,现在来介绍非递归版本的三种遍历方法;
三种遍历均借助顺序栈来完成
先序遍历:
思路:先将根结点入栈,然后循环的进行取栈顶元素,同时出栈,并且将栈顶元素的左右子树入栈,当取栈顶元素失败说明,已经遍历完了,跳出循环;
代码实现:
void TreePreOrderByLoop(TreeNode* root){
if(root == NULL){
return;
}
//1.先把根结点入栈
SeqStack stack;
SeqStackInit(&stack);
SeqStackPush(&stack,root);
//2.循环取栈顶元素,访问,并且出栈,在将根结点的左右子树入栈
TreeNode* top = NULL;
while(1){
int ret = SeqStackTop(&stack,&top);
if(ret == 0){
printf("\n");
//取栈顶元素失败,遍历结束
break;
}
SeqStackPop(&stack);
printf("%c ",top->data);
if(top->rchild != NULL){
SeqStackPush(&stack,top->rchild);
}
if(top->lchild != NULL){
SeqStackPush(&stack,top->lchild);
}
}
return;
}
中序遍历
思路:这里与先序遍历不同的是入栈之后不能立刻访问,需要先找到最左边的一个结点,所以定义一个cur指向root循环的入栈并且让cur指向cur的左子树,当cur为空时,进行出栈,并且同时进行访问,在将cur调整指向此时栈顶元素的右子树即可;
代码实现:
void TreeInOrderByLoop(TreeNode* root){
if(root == NULL){
return;
}
//定义cur指针指向根结点
SeqStack stack;
SeqStackInit(&stack);
TreeNode* cur = root;
while(1){
//1.循环判定cur是否为空,如果不为空,将cur入栈,并且cur指向cur->lchild
while(cur != NULL){
SeqStackPush(&stack,cur);
cur = cur->lchild;
}
//2.如果cur为空,取栈顶元素,访问,出栈
TreeNode* top = NULL;
int ret = SeqStackTop(&stack,&top);
if(ret == 0){
printf("\n");
//此时说明已经没有栈顶元素,遍历完了
return;
}
printf("%c ",top->data);
SeqStackPop(&stack);
//3.让cur指向栈顶元素的右子树,重复循环刚才判空的过程
cur = top->rchild;
}
}
后序遍历:
思路:这里与中序遍历思路相似,不同的是在进行访问之前应该先对栈顶元素进行判断,判断是否能够进行访问;
代码实现如下:
void TreePostOrderByLoop(TreeNode* root){
if(root == NULL){
return;
}
//1.定义一个cur指针指向root
SeqStack stack;
SeqStackInit(&stack);
TreeNode* cur = root;
TreeNode* pre = NULL;//保存上一个访问过的节点
//2.循环判定cur是否为空,如果cur不为空,就将cur入栈,并且cur指向cur->lchild
while(1){
while(cur != NULL){
SeqStackPush(&stack,cur);
cur = cur->lchild;
}
//3.如果cur为空,循环取栈顶元素
TreeNode* top = NULL;
int ret = SeqStackTop(&stack,&top);
if(ret == 0){
printf("\n");
//取栈顶元素失败,遍历结束
return;
}
//4.对栈顶元素进行判定
// a)如果栈顶元素的右子树和上一个访问过的元素相等
// b)或者栈顶元素的右子树为空
// 此时访问栈顶元素,同时进行出栈
//5.如果不满足刚才的条件,就让cur指向栈顶元素的右子树,重复循环
if(top->rchild == pre || top->rchild == NULL){
printf("%c ",top->data);
SeqStackPop(&stack);
pre = top;
}else{
cur = top->rchild;
}
}
}