之前,我们写过二叉树的一些基本操作,大部分是通过递归来实现的。想想看,要是不采用递归的方式,我们该如何对二叉树进行操作?
下面,我们用非递归方式完成二叉树的先序、中序和后序遍历。
1.通过循环实现树的先序遍历
还是我们的那颗二叉树:
我们先想一下,先序遍历的顺序是:根结点 —> 左子树 —> 右子树。只要我们先打印根结点,然后再打印根结点的左右子树,每一个节点都这样循环的进行,直到遍历完所有的结点。
思路:这里,我们建一个栈,先将根结点入栈,对照上图,即 A 入栈,循环的取栈顶元素,出栈,并且访问栈顶元素,即第一次打印 A ,然后先将该元素的右子树入栈 即C,再将左子树 B 入栈,重复上述步骤,直到栈为空,说明遍历结束。
代码如下:
void TreePreOrderByLoop(TreeNode* root)
{
if(root == NULL)
{
return;
}
//1.先把根节点入栈
SeqStack stack;
SeqStackInit(&stack);
SeqStackPush(&stack,root);
//2.循环的取栈顶元素,栈为空,说明循环结束
TreeNode* cur;
while(1)
{
int ret = SeqStackTop(&stack,&cur);
if(ret == 0){
break;
}
//a)取栈顶元素为当前元素;
//b)出栈
SeqStackPop(&stack);
//c)访问当前元素
printf("%c ",cur->data);
//d)将当前点的右子树入栈
if(cur->rchild != NULL)
{
SeqStackPush(&stack,cur->rchild);
}
//e)将当前点的左子树入栈
if(cur->lchild != NULL)
{
SeqStackPush(&stack,cur->lchild);
}
}
printf("\n");
return;
}
2.通过循环实现树的中序遍历
思路:定义一个cur指针,先将cur 指向root,循环判断cur是否为空
如果不为空,入栈,然后将cur指向cur的 lchild;
如果为空,取栈顶元素,出栈并且访问它,再将cur指向栈顶元素的 rchild。直到栈为空,遍历结束。
具体实现代码如下:
void TreeInOrderByLoop(TreeNode* root)
{
if(root == NULL)
{
return;//非法输入
}
SeqStack stack;
SeqStackInit(&stack);
//1.先将cur指向root
TreeNode* cur = root;
//2.循环的判断cur是否为空,
while(1){
while(cur != NULL)
{
// 如果不为空,入栈,然后让cur指向cur的lchild
SeqStackPush(&stack,cur);
cur = cur->lchild;
}
// 如果为空,取栈顶元素,访问出栈,cur指向cur的rchild
TreeNode* top = NULL;
int ret = SeqStackTop(&stack,&top);
if(ret == 0)
{
return;//栈为空,遍历完成
}
printf("%c ",top->data);
SeqStackPop(&stack);
cur = top->rchild;
}
}
3.通过循环实现树的后序遍历
思路:后序遍历和中序遍历思路大体一致,但有一个很重要的区别:当取到栈顶元素后我们不能立即访问它,因为不确定它的右子树是否已经访问过。(后序遍历的顺序:左子树 —> 右子树 —>根节点)。因此,访问栈顶元素需要满足两个条件:1.当前节点右子树为空;2.右子树已经访问过了(即当前节点的上一个节点,后序遍历根节点前一个就是右子树)。
代码如下:
void TreePostOrderByLoop(TreeNode* root)
{
if(root == NULL)
{
return;//非法输入
}
SeqStack stack;
SeqStackInit(&stack);
//1.先让cur指向root
TreeNode* cur = root;
//pre保存上一个访问过的元素
TreeNode* pre = NULL;
//2.循环的判断cur是否为空
while(1){
while(cur != NULL)
{
//3.如果不为空,压栈
SeqStackPush(&stack,cur);
//让cur指向cur的左孩子结点
pre = cur;
cur = cur->lchild;
}
//4.如果为空,循环取栈顶元素,对栈顶元素进行判定,
TreeNode* top;
int ret = SeqStackTop(&stack,&top);
if(ret == 0)
{
//栈为空,说明遍历完成
return;
}
// a)如果栈顶元素的右子树访问过了,和访问的上一个元素是同一个
// b)或者栈顶元素没有右子树
// 就访问栈顶元素,然后出栈
if(top->rchild == NULL || top->rchild == pre)
{
printf("%c ",top->data);
SeqStackPop(&stack);
pre = top;
}
else
{
//5.如果不满足以上条件,就让cur指向栈顶元素的右子树,重复循环
cur = top->rchild;
}
}
return;
}