非递归、递归遍历二叉树!

小鱼儿 2022-04-13 10:29 530阅读 0赞

树的先、中、后、层序的遍历,需要用到栈结构和队结构。

首先来看树本身的定义:

typedef char TElemType;
typedef struct BiTNode
{
TElemType data;
BiTNode *lchild,*rchild; // 左右孩子指针
}BiTNode,*BiTree;

栈的定义如下:

typedef BiTree SElemType; //栈元素为二叉树指针
#define STACK_INIT_SIZE 10 // 存储空间初始分配量
#define STACK_INCREMENT 2 // 存储空间分配增量
struct SqStack
{
SElemType *base; // 在栈构造之前和销毁之后,base的值为NULL
SElemType *top; // 栈顶指针
int stacksize; // 当前已分配的存储空间,以元素为单位
}; // 顺序栈

队的定义如下:

typedef BiTree QElemType//队列元素为二叉树指针;
typedef struct QNode
{
QElemType data;
QNode *next;
}*QueuePtr;

struct LinkQueue
{
QueuePtr front,rear; // 队头、队尾指针
};

先序递归:

void PreOrderTraverse(BiTree T,void(*Visit)(TElemType))
{ //递归先序
if(T) // T不空
{
Visit(T->data); // 先访问根结点
PreOrderTraverse(T->lchild,Visit); // 再先序遍历左子树
PreOrderTraverse(T->rchild,Visit); // 最后先序遍历右子树
}
}

先序非递归:

算法描述:先序是根左右的顺序,首先每次都访问根结点并将其入栈,然后访问其左孩子,再将其左孩子看做是下一个根结点进行访问,若其左孩子的左孩子为空,则出栈(此时栈指针指向最初入栈的结点)。然后访问其右孩子,再将右孩子看做一个根结点依次按照上述方法进行访问。

}
void PreOrderTraverse1(BiTree T,void (*Visit)(TElemType))
{//非递归(利用栈)实现先序遍历
SqStack S;
InitStack(S);
while(T||!StackEmpty(S))
{
if (T)
{
Visit(T->data);//第一个访问的永远是根,只要非空就输出
Push(S,T);//入栈根结点,若分支为空,方便返回路径
T=T->lchild;//遍历左分支
}
else//左子树为空,说明已经遍历完根和左了,该右了
{
Pop(S,T);//返回到栈顶元素
T=T->rchild;//访问它的右孩子
}
}
}

中序递归:

void InOrderTraverse(BiTree T,void(*Visit)(TElemType))
{ //递归中序
if(T)
{
InOrderTraverse(T->lchild,Visit); // 先中序遍历左子树
Visit(T->data); // 再访问根结点
InOrderTraverse(T->rchild,Visit); // 最后中序遍历右子树
}
}

中序非递归:

算法描述:首先入栈根结点,若根结点的左孩子不空,就入栈左孩子,依次循环入栈左孩子,若发现左孩子的左孩子为空,则出栈弹出栈顶元素(此时其实访问到了最后一个左孩子,已是叶子结点了),访问其结点的值,再继续向右走,向右走的思路和上面一样,等于是在右子树上继续进行左中右顺序的访问。直到栈空为止。

void InOrderTraverse1(BiTree T,void (*Visit)(TElemType))
{//非递归(利用栈)实现中序遍历
SqStack S;
InitStack(S);
while(T||!StackEmpty(S))
{
if (T)
{
Push(S,T);//入栈根结点
T=T->lchild;//遍历左分支
}
else
{
Pop(S,T);
Visit(T->data);
T=T->rchild;
}
}
}

后序递归:

void PostOrderTraverse(BiTree T,void(*Visit)(TElemType))
{ //递归中序
if(T)
{
PostOrderTraverse(T->lchild,Visit); // 先中序遍历左子树
PreOrderTraverse1(T->rchild,Visit); // 最后中序遍历右子树
Visit(T->data); // 再访问根结点
}
}

后序非递归:

算法描述:为了使输出的形式保持左右根的顺序,那么必须使得所有的左子树访问完成,然后所有的右子树访问完成后再访问根。为了准确描述,需要重新定义两个记录指针,cur和pre,前者是指示当前指针,后者是指向前一次位置的指针。遍历时首先先将根节点入栈,每次取栈顶元素的指针( 即top-1)为cur。这样一来可总结为:

若,

{

1、当前左孩子和右孩子均为空(只有根节点)。

2、当前的结点已经访问过。(下面有说明)

}

{则指针访问该结点的值;}

否则(说明有左右孩子),

{哪个孩子非空就入栈哪个孩子;}

什么叫已经访问过的结点?这里说明一下。根据上述的压栈顺序,比如我们现在分别压入根a,右a,左a。假设现在左a和右a都没有孩子了,这个子树已经是树的结尾了。那么栈顶的前三个元素依次为左a,右a,根a。首先取栈顶元素左a,发现其已经是叶子节点了,没有左右孩子,直接访问,退栈到右a,发现右a也没有左右孩子,也可以访问,然后退栈到根a,这时问题就来了,根a是有孩子的啊(左a和右a),理论上是不能访问的,可是我们已经按照左右根的顺序访问过了它的孩子,应该轮到他了呀。这里就需要对已经访问的结点做出判断,即满足pre=cur->lchild ||pre=cur->rchild时也可以访问。

void PostOrderTraverse1(BiTree T,void(*Visit)(TElemType))
{//对于任意节点,先将其入栈。如果它没有左孩子和右孩子则直接访问它;若它有
// 左孩子或者右孩子,但是已经被访问过了(是否访问用两个指针之间的关系判断)也可以访问它;
//如果都不是上述两种情况,则先访问它的右孩子,再访问它的左孩子,以此来保证能够左、右、根的输出。
SqStack S;
InitStack(S);
BiTree cur;//当前指针
BiTree pre=NULL;//前一个指针
Push(S,T);//根节点入栈
while(!StackEmpty(S))//只要栈不空
{//如果当前没有左孩子和右孩子,或者说当前节点是已经访问过的
cur=GetTop(S,T);//每次都取栈顶元素
if ((cur->lchild==NULL && cur->rchild==NULL)||
(pre!=NULL && (pre==cur->lchild)|| pre==cur->rchild))
{
Visit(cur->data);
Pop(S,T);//出栈下移栈顶指针,准备下次访问
pre=cur;//记录走过的路径
}
else
{//保证压栈的顺序为先右后左,才能左根右的输出
if (cur->rchild!=NULL)
Push(S,cur->rchild);
if (cur->lchild!=NULL)
Push(S,cur->lchild);
}
}

层序遍:

算法描述:层序遍历需要用到队列,访问顺序是子根,左孩子1,右孩子1,左孩子1的左孩子和右孩子,右孩子1的左孩子和右孩子………….由于队列具有先进先出的特点,所以我们入队的顺序就是访问结点的顺序。只要哪个孩子不空就入队哪个孩子,出队时直接出队就行,顺序就会保持。

void LevelOrderTraverse(BiTree T,void(*Visit)(TElemType))
{
LinkQueue q;
QElemType a;
if(T)
{
InitQueue(q); // 初始化队列q
EnQueue(q,T); // 根指针入队
while(!QueueEmpty(q)) // 队列不空
{
DeQueue(q,a); // 出队元素(指针),赋给a
Visit(a->data); // 访问a所指结点
if(a->lchild!=NULL) // a有左孩子
EnQueue(q,a->lchild); // 入队a的左孩子
if(a->rchild!=NULL) // a有右孩子
EnQueue(q,a->rchild); // 入队a的右孩子
}
printf(“\n”);
}
}

下面是代码的全部实现:

其中头文件head.h为如下定义:

#include
#include
#include // malloc()等
#include // INT_MAX等
#include // EOF(=^Z或F6),NULL
#include // atoi()
#include // eof()
#include // floor(),ceil(),abs()
#include // exit()
#include // cout,cin
// 函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
typedef int Status; // Status是函数的类型,其值是函数结果状态代码,如OK等
typedef int Boolean; // Boolean是布尔类型,其值是TRUE或FALSE

  1. #include "head.h"//头文件
  2. //树结构定义
  3. typedef char TElemType;
  4. TElemType Nil=' ';
  5. typedef struct BiTNode
  6. {
  7. TElemType data;
  8. BiTNode *lchild,*rchild; // 左右孩子指针
  9. }BiTNode,*BiTree;
  10. /*-------栈操作开始-------*/
  11. typedef BiTree SElemType; //栈元素为二叉树指针
  12. #define STACK_INIT_SIZE 10 // 存储空间初始分配量
  13. #define STACK_INCREMENT 2 // 存储空间分配增量
  14. struct SqStack
  15. {
  16. SElemType *base; // 在栈构造之前和销毁之后,base的值为NULL
  17. SElemType *top; // 栈顶指针
  18. int stacksize; // 当前已分配的存储空间,以元素为单位
  19. }; // 顺序栈
  20. void InitStack(SqStack &S)
  21. { // 构造一个空栈S
  22. if(!(S.base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType))))
  23. exit(OVERFLOW); // 存储分配失败
  24. S.top=S.base;
  25. S.stacksize=STACK_INIT_SIZE;
  26. }
  27. void Push(SqStack &S,SElemType e)
  28. { // 插入元素e为新的栈顶元素
  29. if(S.top-S.base>=S.stacksize) // 栈满,追加存储空间
  30. {
  31. S.base=(SElemType *)realloc(S.base,(S.stacksize+STACK_INCREMENT)*sizeof(SElemType));
  32. if(!S.base)
  33. exit(OVERFLOW); // 存储分配失败
  34. S.top=S.base+S.stacksize;
  35. S.stacksize+=STACK_INCREMENT;
  36. }
  37. *(S.top)++=e;
  38. }
  39. Status Pop(SqStack &S,SElemType &e)
  40. { // 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
  41. if(S.top==S.base)
  42. return ERROR;
  43. e=*--S.top;
  44. return OK;
  45. }
  46. BiTree GetTop(SqStack S,SElemType &e)
  47. { // 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
  48. if(S.top>S.base)
  49. {
  50. e=*(S.top-1);
  51. return e;
  52. }
  53. else
  54. return NULL;
  55. }
  56. Status StackEmpty(SqStack S)
  57. { // 若栈S为空栈,则返回TRUE,否则返回FALSE
  58. if(S.top==S.base)
  59. return TRUE;
  60. else
  61. return FALSE;
  62. }
  63. /*---------栈操作结束--------------*/
  64. /*---------队操作开始----------*/
  65. typedef BiTree QElemType;
  66. typedef struct QNode
  67. {
  68. QElemType data;
  69. QNode *next;
  70. }*QueuePtr;
  71. struct LinkQueue
  72. {
  73. QueuePtr front,rear; // 队头、队尾指针
  74. };
  75. void InitQueue(LinkQueue &Q)
  76. { // 构造一个空队列Q
  77. if(!(Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode))))
  78. exit(OVERFLOW);
  79. Q.front->next=NULL;
  80. }
  81. Status QueueEmpty(LinkQueue Q)
  82. { // 若Q为空队列,则返回TRUE,否则返回FALSE
  83. if(Q.front->next==NULL)
  84. return TRUE;
  85. else
  86. return FALSE;
  87. }
  88. void EnQueue(LinkQueue &Q,QElemType e)
  89. { // 插入元素e为Q的新的队尾元素
  90. QueuePtr p;
  91. if(!(p=(QueuePtr)malloc(sizeof(QNode)))) // 存储分配失败
  92. exit(OVERFLOW);
  93. p->data=e;
  94. p->next=NULL;
  95. Q.rear->next=p;
  96. Q.rear=p;
  97. }
  98. Status DeQueue(LinkQueue &Q,QElemType &e)
  99. { // 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR
  100. QueuePtr p;
  101. if(Q.front==Q.rear)
  102. return ERROR;
  103. p=Q.front->next;
  104. e=p->data;
  105. Q.front->next=p->next;
  106. if(Q.rear==p)
  107. Q.rear=Q.front;
  108. free(p);
  109. return OK;
  110. }
  111. /*---------队操作结束----------*/
  112. /*------二叉树操作开始------*/
  113. void visitT(TElemType e)
  114. {
  115. printf("%c ",e);
  116. }
  117. void PreOrderTraverse(BiTree T,void(*Visit)(TElemType))
  118. { //递归先序
  119. if(T) // T不空
  120. {
  121. Visit(T->data); // 先访问根结点
  122. PreOrderTraverse(T->lchild,Visit); // 再先序遍历左子树
  123. PreOrderTraverse(T->rchild,Visit); // 最后先序遍历右子树
  124. }
  125. }
  126. void PreOrderTraverse1(BiTree T,void (*Visit)(TElemType))
  127. {//非递归(利用栈)实现先序遍历
  128. SqStack S;
  129. InitStack(S);
  130. while(T||!StackEmpty(S))
  131. {
  132. if (T)
  133. {
  134. Visit(T->data);//第一个访问的永远是根,只要非空就输出
  135. Push(S,T);//入栈根结点,若分支为空,方便返回路径
  136. T=T->lchild;//遍历左分支
  137. }
  138. else//左子树为空,说明已经遍历完根和左了,该右了
  139. {
  140. Pop(S,T);//返回到栈顶元素
  141. T=T->rchild;
  142. }
  143. }
  144. }
  145. void InOrderTraverse(BiTree T,void(*Visit)(TElemType))
  146. { //递归中序
  147. if(T)
  148. {
  149. InOrderTraverse(T->lchild,Visit); // 先中序遍历左子树
  150. Visit(T->data); // 再访问根结点
  151. InOrderTraverse(T->rchild,Visit); // 最后中序遍历右子树
  152. }
  153. }
  154. void InOrderTraverse1(BiTree T,void (*Visit)(TElemType))
  155. {//非递归(利用栈)实现中序遍历
  156. SqStack S;
  157. InitStack(S);
  158. while(T||!StackEmpty(S))
  159. {
  160. if (T)
  161. {
  162. Push(S,T);//入栈根结点
  163. T=T->lchild;//遍历左分支
  164. }
  165. else
  166. {
  167. Pop(S,T);
  168. Visit(T->data);
  169. T=T->rchild;
  170. }
  171. }
  172. }
  173. void PostOrderTraverse(BiTree T,void(*Visit)(TElemType))
  174. { //递归中序
  175. if(T)
  176. {
  177. PostOrderTraverse(T->lchild,Visit); // 先中序遍历左子树
  178. PostOrderTraverse(T->rchild,Visit); // 最后中序遍历右子树
  179. Visit(T->data); // 再访问根结点
  180. }
  181. }
  182. //非递归后序遍历
  183. void PostOrderTraverse1(BiTree T,void(*Visit)(TElemType))
  184. {//对于任意节点,先将其入栈。如果它没有左孩子和右孩子则直接访问它;若它有
  185. // 左孩子或者右孩子,但是已经被访问过了(是否访问用两个指针之间的关系判断)也可以访问它;
  186. //如果都不是上述两种情况,则先访问它的右孩子,再访问它的左孩子,以此来保证能够左、右、根的输出。
  187. SqStack S;
  188. InitStack(S);
  189. BiTree cur;//当前指针
  190. BiTree pre=NULL;//前一个指针
  191. Push(S,T);//根节点入栈
  192. while(!StackEmpty(S))//只要栈不空
  193. {//如果当前没有左孩子和右孩子,或者说当前节点是已经访问过的
  194. cur=GetTop(S,T);//每次都取栈顶元素
  195. if ((cur->lchild==NULL && cur->rchild==NULL)||
  196. (pre!=NULL && (pre==cur->lchild)|| pre==cur->rchild))
  197. {
  198. Visit(cur->data);
  199. Pop(S,T);//出栈下移栈顶指针,准备下次访问
  200. pre=cur;//记录走过的路径
  201. }
  202. else
  203. {//保证压栈的顺序为先右后左,才能左右根的输出
  204. if (cur->rchild!=NULL)
  205. Push(S,cur->rchild);
  206. if (cur->lchild!=NULL)
  207. Push(S,cur->lchild);
  208. }
  209. }
  210. }
  211. void LevelOrderTraverse(BiTree T,void(*Visit)(TElemType))
  212. {
  213. LinkQueue q;
  214. QElemType a;
  215. if(T)
  216. {
  217. InitQueue(q); // 初始化队列q
  218. EnQueue(q,T); // 根指针入队
  219. while(!QueueEmpty(q)) // 队列不空
  220. {
  221. DeQueue(q,a); // 出队元素(指针),赋给a
  222. Visit(a->data); // 访问a所指结点
  223. if(a->lchild!=NULL) // a有左孩子
  224. EnQueue(q,a->lchild); // 入队a的左孩子
  225. if(a->rchild!=NULL) // a有右孩子
  226. EnQueue(q,a->rchild); // 入队a的右孩子
  227. }
  228. printf("\n");
  229. }
  230. }
  231. void InitBiTree(BiTree &T)
  232. { // 操作结果:构造空二叉树T
  233. T=NULL;
  234. }
  235. void CreateBiTree(BiTree &T)
  236. { //递归先序生成二叉树
  237. TElemType ch;
  238. scanf("%c",&ch);
  239. if(ch==Nil) // 空
  240. T=NULL;
  241. else
  242. {
  243. T=(BiTree)malloc(sizeof(BiTNode)); // 生成根结点
  244. if(!T)
  245. exit(OVERFLOW);
  246. T->data=ch;
  247. CreateBiTree(T->lchild); // 构造左子树
  248. CreateBiTree(T->rchild); // 构造右子树
  249. }
  250. }
  251. /*-------二叉树操作结束----------*/
  252. /************************************************************************/
  253. /* 主函数开始 */
  254. /************************************************************************/
  255. void main()
  256. {
  257. BiTree T;
  258. InitBiTree(T);
  259. printf("先序输入构造二叉树:\n");
  260. CreateBiTree(T);
  261. printf("利用队----层序遍历----二叉树:\n");
  262. LevelOrderTraverse(T,visitT);
  263. printf("\n");
  264. printf("递归法---先序遍历---二叉树:\n");
  265. PreOrderTraverse(T,visitT);
  266. printf("\n");
  267. printf("非递归---先序遍历---二叉树:\n");
  268. PreOrderTraverse1(T,visitT);
  269. printf("\n");
  270. printf("递归法---中序遍历---二叉树:\n");
  271. InOrderTraverse(T,visitT);
  272. printf("\n");
  273. printf("非归法---中序遍历---二叉树:\n");
  274. InOrderTraverse(T,visitT);
  275. printf("\n");
  276. printf("递归法---后序遍历---二叉树:\n");
  277. PostOrderTraverse(T,visitT);
  278. printf("\n");
  279. printf("非递归---后序遍历---二叉树:\n");
  280. PostOrderTraverse1(T,visitT);
  281. printf("\n");
  282. }

1357180359_5454.png

发表评论

表情:
评论列表 (有 0 条评论,530人围观)

还没有评论,来说两句吧...

相关阅读