二叉树的广义表创建及中序遍历、后序遍历、层次遍历的非递归算法(C语言)
广义表创建二叉树
关于用广义表的形式表示二叉树的形式如下
①广义表中的一个字母代表一个结点的数据信息。
②每个根结点作为由子树构成的表的名字放在义表的前面。
③每个结点的左子树与右子树之间用逗号分开。若结点只有右子树面无左子树,则该逗号不能省略。
④在整个广义表的末尾加一个特殊符号(如“@”)作为结束标志。
下面先用自然语言描述算法如下。
依次从广义表中取得-个元素,并对取得的元素做如下相应的处理。
①若当前取得的元素为字母,则按如下规则建立一个新的(链)结点。
a)若该结点为二叉树的根结点,则将该结点的地址送T。
b)若该结点不是二叉树的根结点,则将该结点作为左孩子(若标志flag为1)或者右子若标志flag为2)链接到其双亲结点上(此时双亲结点的地址在栈顶位置)。
②若当前取得的元素为左括号“(”,则表明一个子表开始,将标志flag置为1,同时将面那个结点的地址进栈。
③若当前取得的元素为右括号“)”,则表明一个子表结束,做退栈操作。
④若当前取得的元素为逗号,则表明以左孩子为根的子树处理完毕,接着应该处理孩子为根的子树,将标志flag置为2。
如此处理广义表中的每一个元素,直到取得广义表的结束符号“@”为止。
二叉树的中序遍历(非递归)
算法的核心思想是:
当P所指的结点不为空时.则将该结点所在链结点的地址进栈,然后再将”指向该结点的左孩子结点:当P所指的结点为空时则从堆栈中退出栈项元素(某个结宜的地址)送p.并访问该结点,然后再将p指向该结点的右孩子结点。重复上述过程,直到P为NULL.并且堆栈也为空,遍历结束。
二叉树的后序遍历(非递归)
下面再讨论后序遍历的非递归算法:
在对二又树进行后序遍历的过程中,当指针p指向某一个结点时,不能马上对它进行访问,而要先遍历它的左子树,因而要将此结点的地址进栈;当其左子树遍历完毕之后,再次搜索到该结点时(该结点的地址通过退栈得到),还不能对它进行访问,还需要遍历它的右子树,所以,再一次将此结点的地址进栈。只有当该结点的右子树被遍历后回到该结点,才访问该结点。为了标明某结点是否可以被访问,引人一个标志变量flag,
并有
1、flag=0表示该结点暂不访问
2、flag =1表示该结点可以访问
标志flag的值随同进栈结点的地址起进栈和出栈。因此,算法中设置了两个空间足够的堆栈,其中,STACK1[0… M- 1 ]存放进栈结点的地址,STACK2[0… M- 1]存放相应的标志flag的值,两个堆栈使用同一栈顶指针top,top的初值为一1。
二叉树的按层次遍历
下面测试的算法中使用了一个顺序存储结构队列QUEUE[0. . M- 1],(不妨假设队列的空间足够大).front与rear分别为队头指针和队尾指针。遍历进行之前先把二叉树根结点的存储地址进队.然后依次从队列中退出一个元素(结点的存储地址);每退出一个元素,先访问该元素所指的结点.然后依次把该结点的左孩子结点(若存在的话)与右孩子结点(若存在的话)的地址依次进队。如此重复下去,直到队列为空。此时,访问结点的次序就是按层次遍历该二叉树的次序。
有关中序遍历,后序遍历,层次遍历的非递归算法和二叉树的广义表创建一起测试
#include<stdio.h> #include<stdlib.h> #define M 100 typedef struct BTREE { char data; struct BTREE *lchild, *rchild; }BT, *LBTREE; LBTREE Creat() { LBTREE STACK[M], p = NULL, T = NULL; int flag, top = -1; char ch; while (1) { scanf_s("%c", &ch); switch (ch) { case ‘(‘: //左括号则下一个读取字符入栈 STACK[++top] = p; flag = 1; break; case ‘)‘: //右括号则栈顶元素出栈 top--; break; case ‘,‘: flag = 2; break; case ‘@‘: //返回T,表示创建完毕 return T; default: //读到字母时 p = (LBTREE)malloc(sizeof(BT)); p->data = ch; p->lchild = NULL; p->rchild = NULL; if (T == NULL) T = p; else if (flag == 1) STACK[top]->lchild = p; else if (flag == 2) STACK[top]->rchild = p; } } } void INORDER(LBTREE T) //中序遍历非递归 { LBTREE STACK[M], p = T; int top = -1; if (T != NULL) { do { while (p != NULL) //这里不可以是 (p=p->lchild)!=NULL { STACK[++top] = p; p = p->lchild; } p = STACK[top--]; //出栈 printf("%c", STACK[top + 1]->data); p = p->rchild; } while (!(p == NULL && top == -1)); } } void LAYERORDER(LBTREE T) //按层次遍历 { LBTREE QUEUE[M], p; int front, rear; if (T != NULL) //队列 { QUEUE[0] = T; front = -1; rear = 0; while (front < rear) { p = QUEUE[++front]; printf("% c", p->data); if (p->lchild != NULL) QUEUE[++rear] = p->lchild; if (p->rchild != NULL) QUEUE[++rear] = p->rchild; } } } void POSTORED(LBTREE T) //后序遍历的非递归算法 { LBTREE STACK1[M]; //创建了两个栈,一个用来存放结点的地址 int STACK2[M], flag, top = -1; //另一个用来存放数字,1:可以读取,0:不可读取 LBTREE p = T; if (T != NULL) { do { while (p != NULL) { STACK1[++top] = p; STACK2[top] = 0; p = p->lchild; } p = STACK1[top]; flag = STACK2[top--]; if (flag == 0) { STACK1[++top] = p; STACK2[top] = 1; p = p->rchild; } else { printf("%c", p->data); p = NULL; } } while (!(p == NULL && top == -1)); } } void Distroyb(LBTREE T) //销毁二叉树(同样使用递归的方式) { if (T != NULL) { Distroyb(T->lchild); Distroyb(T->rchild); free(T); } } void main(void) { LBTREE T = NULL; puts("输入二叉树数据"); T=Creat(T); puts("按层次遍历输出:"); LAYERORDER(T); puts(""); puts("非递归中序遍历:"); INORDER(T); puts(""); puts("非递归后续遍历:"); POSTORED(T); Distroyb(T); //销毁二叉树 }
输入:
A(B(D,E(G)),C(F(,H)))@
以下是结果:
嗝~
codeloop