在递归中,我们需要使用函数调用栈,模拟递归的执行过程,借助于栈就能将递归改为非递归形式。
先序遍历:
//先序遍历
void PreOrderTraverse(TreeNode* root) {
if (root == NULL) {
printf("树不存在,无法遍历\n");
return;
}
Stack* s = (Stack*)malloc(sizeof(Stack));
if (s == NULL) {
perror("malloc");
return;
}
s->topofstack = -1;
//在先序遍历时,模拟递归时的函数调用栈,首先处理当前的子树的根节点
//由于非递归没有回溯,所以必须使用栈将处理过的结点保存在栈中,这样后续
//遍历右子树时可以通过栈中的根节点找到右子树。
//由于树的遍历是深度优先,所以使用栈
TreeNode* p = root;
while (!IsEmpty(s) || p) {//当栈不为空或者p还没有到叶节点时,说明还需要继续处理
if (p) {//如果p结点不为空,那么先处理p结点,然后处理它的左子树
printf("%d ", p->val);
push(s, p);//为了之后能找到p的右子树,需要将p存储在栈中
p = p->leftchild;
}
else {//当p为NULL时,弹出栈顶元素,栈顶元素没有左子树,所以开始遍历右子树
//在递归中,当遇到NULL时,递归会回到上一层访问右子树
p = pop(s);
p = p->rightchild;
}
}
}
中序遍历:
//中序遍历
void InOrderTraverse(TreeNode* root) {
if (root == NULL) {
printf("树不存咋,无法遍历\n");
return;
}
Stack* s = (Stack*)malloc(sizeof(Stack));
if (s == NULL) {
perror("malloc");
return;
}
s->topofstack = -1;
TreeNode* p = root;
while (!IsEmpty(s) || p) {//当栈不为空时说明栈中结点的右子树还没处理,p不为空说明左子树还未处理完
if (p) {
//这步类似于递归遍历左子树的过程
push(s, p);//将当前顶点入栈
p = p->leftchild;//处理它的左子树
}
else {
p = pop(s);//弹出栈顶元素,相当于递归中返回上一层,然后处理该结点
printf("%d ", p->val);
p = p->rightchild;//接着遍历右子树
}
}
}
后序遍历:
//后序遍历
void PostOrderTraverse(TreeNode* root) {
if (root == NULL) {
printf("树不存在,无法遍历\n");
return;
}
Stack* s = (Stack*)malloc(sizeof(Stack));
if (s == NULL) {
perror("malloc");
return;
}
s->topofstack = -1;
TreeNode* p = root;
//后序遍历需要一个域来标记结点是否已经访问的原因是
//在递归中,当底层结点访问结束后,函数会自己返回上一层继续执行
//但是在循环中,我们只能使用栈来模拟这样的递归过程,也就是说
//我们需要将上层结点保存在栈中以便后序访问,但是在代码中
//我们只有向下的逻辑,如果不使用visit域,那么右子树访问结束后
//返回到上一层,接下来又会从上一层访问它的右子树,这就会陷入循环
while (!IsEmpty(s) || p) {//栈不为空或者p不为NULL,说明还有结点需要处理
if (p) {//如果p不为NULL,那么将p入栈,遍历它的左子树
push(s, p);
p = p->leftchild;
}
else {//当p为空时,说明左侧已经遍历结束,该遍历右子树
//这里不能将栈顶元素出栈,因为后序遍历需要先遍历右子树,如果
//这里出栈,那么处理完右子树就丢失这个结点了
p = top(s);
//如果p没有右子树,那么根据后序遍历规则,此时就处理p
//或者p的右子树已经处理过,这时也该处理p
if (p->rightchild == NULL || p->rightchild->visit == 1) {
printf("%d ", p->val);
p->visit = 1;//访问过后将visit置为1
pop(s);//处理当前根节点后,就将其出栈
p = NULL;//然后将p置为NULL
}
else {
p = p->rightchild;//如果不满足上面的条件,就需要先遍历右子树
}
}
}
}
测试代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode {
int val;
int visit;//在后序遍历时用来标记此节点是否已经访问
struct TreeNode* leftchild;
struct TreeNode* rightchild;
}TreeNode;
//建立一个二叉树
TreeNode* Insert(TreeNode* root, int val) {
if (root == NULL) {
TreeNode* tmp = (TreeNode*)malloc(sizeof(TreeNode));
if (tmp == NULL) {
perror("malloc");
return NULL;
}
tmp->leftchild = NULL;
tmp->rightchild = NULL;
tmp->val = val;
tmp->visit = 0;
return tmp;
}
else {
if (val < root->val) {
root->leftchild = Insert(root->leftchild, val);
}
else {
root->rightchild = Insert(root->rightchild, val);
}
return root;
}
}
//实现栈
typedef struct Stack {
TreeNode* arr[15];
int topofstack;
}Stack;
int IsEmpty(Stack* s) {
return s->topofstack == -1;
}
void push(Stack* s, TreeNode* k) {
s->arr[++s->topofstack] = k;
}
TreeNode* pop(Stack* s) {
return s->arr[s->topofstack--];
}
TreeNode* top(Stack* s) {
return s->arr[s->topofstack];
}
int main() {
TreeNode* root = NULL;
root = Insert(root, 5);
for (int i = 0; i < 5; i++) {
root = Insert(root, i);
}
for (int i = 6; i < 10; i++) {
root = Insert(root, i);
}
PostOrderTraverse(root);
printf("\n---------------------------\n");
return 0;
}