二叉树的非递归遍历
二叉树遍历的三种递归遍历的方法都比较直观,这里说一下他们的三种非递归遍历的方法。
一.先序遍历的非递归算法
1.栈stack初始化
2.循环直到栈stack为空且p为空
(1)如果p非空则
a.访问p所指的当前根节点;
b.将p值保存到栈stack中;
c.将p指向其左子树的根节点,继续遍历其左子树;
(2)如果p为空则
a.从栈stack中退出元素赋给p,回溯至父亲节点
b.将p指向其右子树的根节点,继续遍历右子树
二.中序遍历的非递归算法
1.栈stack初始化
2.循环直到栈stack为空且p为空
(1)如果p非空则
a.将p值保存到栈stack中
b.将p指向其左子树的根节点,继续遍历其左子树
(2)如果p为空则
a.从栈stack中退出元素赋给p,回溯至父亲节点
b.访问p所指向的当前节点
c.将p指向其右子树,继续遍历其右子树
三.后序遍历的非递归算法
在后序遍历算法中,在访问完左子树后,需要跳到右子树,右子树访问完毕了在回溯到根节点并访问它。因此,在后序遍历过程中,节点需要入栈俩次,并出栈俩次。第一次出栈只遍历完左子树,第二次出栈表示遍历完右子树。
#include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct node { int data; struct node *left; struct node *right; int tag; //tag标志,用来来后序遍历的时候标记左子树和右子树被访问过。 } Node; Node stack[100];//用来保存访问过的节点,相当于栈。 Node *make_node(int data) { Node *pn = (Node *)malloc(sizeof(Node)); pn->data = data; pn->left = NULL; pn->right = NULL; return pn; } Node *make_tree(Node *root, int data) { if (root == NULL) { root = make_node(data); } else if (data < root->data) root->left = make_tree(root->left, data); else root->right = make_tree(root->right, data); return root; } void preorder(Node *pn) { if (pn) { printf("%d ", pn->data); preorder(pn->left); preorder(pn->right); } } //前序遍历的时候先是根节点,然后是左子树,最后是右子树 void preorder1(Node *pn) { memset(stack, 0, sizeof(stack));//栈清空 int top = -1;//栈顶置为-1 while (top != -1 || pn != NULL) { while (pn != NULL) { printf("%d ", pn->data);//访问根节点 stack[++top] = *pn;//入栈,保存当前节点到栈中 pn = pn->left;//继续访问左子树 } if (top != -1) { pn = &stack[top--];//回溯到父亲节点 pn = pn->right;//访问右子树 } } } void inorder(Node *pn) { if (pn) { inorder(pn->left); printf("%d ", pn->data); inorder(pn->right); } } //中序遍历的顺序先是左子树,然后根节点,最后是右子树 void inorder1(Node *pn) { memset(stack, 0, sizeof(stack)); int top = -1; while (top != -1 || pn != NULL) { while (pn != NULL) { stack[++top] = *pn;//左子树依次入栈 pn = pn->left; } if (top != -1) { pn = &stack[top--];//回溯至父亲节点 printf("%d ", pn->data); pn = pn->right;//访问右子树 } } } void postorder(Node *pn) { if (pn) { postorder(pn->left); postorder(pn->right); printf("%d ", pn->data); } } //后序遍历的顺序先是左子树,然后右子树,最后根节点。所以在访问完左子树后,需要跳到右子树,右子树访问完了再回溯至根节点 //在访问它,因此在后序遍历的过程中需要入栈和出栈俩次,所以需要一个额外的标志。 void postorder1(Node *pn) { memset(stack, 0, sizeof(stack)); int top = -1; do { while (pn != NULL) { stack[++top] = *pn;//入栈,保存当前节点 pn->tag = 1;//表示左子树已经访问了 pn = pn->left; } while((top != -1) && (stack[top].tag == 2)) {//tag == 2表示左子树和右子树都访问了,可以打印节点的信息了 printf("%d ", stack[top].data); top--; } if (top != -1) { pn = &stack[top];//出栈回溯至父亲节点 pn->tag = 2;//表示该节点的右子树访问过了 pn = pn->right; } } while (top != -1);//栈为空就终止, } int main(void) { int a[] = {10, 6, 4, 8, 5, 20, 18, 25, 28}; Node *root = NULL; int i; for (i = 0; i < 9; i++) root = make_tree(root, a[i]); preorder(root); printf("\n"); preorder1(root); printf("\n"); inorder(root); printf("\n"); inorder1(root); printf("\n"); postorder(root); printf("\n"); postorder1(root); printf("\n"); return 0; }