当前位置:   article > 正文

数据结构|二叉树的遍历_pta二叉树的遍历作者高天

pta二叉树的遍历作者高天

不得不说,第一次接触数据结构,二叉树的这一章让我多少有点觉得吃力,里面的代码实现,琢磨了几天才做出来,今天做一个整体代码总结,希望能帮助到大家。

前导知识

我们知道,一个满二叉树或完全二叉树,我们是完全可以使用顺序存储结构的。

因为我们满二叉树或者完全二叉树的结点个数较为完整,因为完全二叉树接近满二叉树,满二叉树是完全二叉树的特例,所以我们可以使用满二叉树的形式存储。

对于完全二叉树中有些结点无法与满二叉树完全对应,所以我们适当添加一些空结点来对应满二叉树。

但是如果是一个普通二叉树,仍然采用顺序存储结构的话,就会非常浪费空间,当只有右单分支的情况下,我们需要分配2的h次方-1的空结点,所以为了存储空间的节约,我们采用链式存储结构存储一棵普通二叉树。

二叉树的遍历分为递归和非递归遍历。

递归遍历分为先序遍历,后序遍历,中序遍历

创建一棵二叉树

这里建议使用getChar(),因为getChar()可以避免空格和回车字符,scanf必须在占位符前面加上一个空格才能避免把回车当作一个字符

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <malloc.h>
  4. #include <stdbool.h>
  5. #define MAXSIZE 100
  6. typedef struct TreeNode{
  7. char data;
  8. struct TreeNode *lchild;
  9. struct TreeNode *rchild;
  10. }TreeNode;
  11. TreeNode* create(TreeNode *treeList){
  12. char value;
  13. printf("请输入一个字符\n");
  14. scanf(" %c",&value);//注意啊,回车也会被当作一个字符
  15. if(value=='#'){
  16. treeList=NULL;
  17. }else{
  18. treeList=(TreeNode *)malloc(sizeof(TreeNode));
  19. treeList->data=value;
  20. //由于每一层都有返回值,
  21. //所以我们必须保存每次递归后的左右节点的值,否则链表就会连接不起来
  22. treeList->lchild=create(treeList->lchild);
  23. treeList->rchild=create(treeList->rchild);
  24. }
  25. return treeList;
  26. }

先序递归遍历

先输出根结点,然后遍历每个结点的左结点,直到左结点为空,我们在循环遍历右结点,以此类推直到遍历整个二叉树

  1. void PreOrderOut(TreeNode *treeList){//先序输出
  2. if(treeList!=NULL){
  3. printf("先根结点:%c\n",treeList->data);//输出根结点
  4. PreOrderOut(treeList->lchild);//遍历左结点,直到左结点为空
  5. PreOrderOut(treeList->rchild);//遍历右结点,直到右结点为空
  6. }
  7. }

中序和后序递归遍历也是再这个基础之上进行变换,是不是很简单,三句话就解决问题,只要你逻辑够清楚,就很好理解了。

  1. void InOrderOut(TreeNode *treeList){//中序输出
  2. if(treeList!=NULL){
  3. InOrderOut(treeList->lchild);
  4. printf("中序根结点:%c\n",treeList->data);
  5. InOrderOut(treeList->rchild);
  6. }
  7. }
  1. void LastOrderOut(TreeNode *treeList){//后序输出
  2. if(treeList!=NULL){
  3. LastOrderOut(treeList->lchild);
  4. LastOrderOut(treeList->rchild);
  5. printf("后续根结点:%c\n",treeList->data);
  6. }
  7. }

层次遍历

所谓层次遍历,就是从根结点开始,从上到下,从左到右依次遍历二叉树每个结点

这里为了方便,我们采用循环队列来存储结点,队列的特点是先进先出,刚好满足我们的层次遍历的特点。依次从根节点开始,将第一个结点存储到队列中,队列的队尾下标加1,然后再输出,然后再保存左结点和右节点,之后再输出左节点,保存左节点的孩子结点,队列的队头下标加1,然后再输出右节点,然后保存右节点的孩子节点,以保证每次循环不丢失下一个结点的位置,直到队头队尾位置相等了,就说明此时已经遍历完毕了。

  1. typedef struct TreeNode{//二叉树存储结构
  2. char data;
  3. struct TreeNode *lchild;
  4. struct TreeNode *rchild;
  5. }TreeNode;
  6. typedef struct QueueNode{//队列存储结构
  7. TreeNode *data[MAXSIZE];
  8. int top;
  9. int base;
  10. }QueueNode;
  11. void push1(QueueNode *queuenode,TreeNode *treeList){//入队方法
  12. printf("进队列:%c\n",treeList->data);
  13. if(treeList!=NULL&&((queuenode->top+1)%MAXSIZE!=queuenode->base)){
  14. queuenode->data[queuenode->top]=treeList;
  15. //printf("打印一下:%c,%d\n",queuenode->data[queuenode->top]->data,queuenode->top);
  16. queuenode->top=(queuenode->top+1)%MAXSIZE;
  17. }
  18. }
  19. TreeNode* pop1(QueueNode *queuenode){//出队方法
  20. TreeNode *treenode;
  21. if(queuenode->top!=queuenode->base){//只要队头不等于队尾,就代表当前队列还不为空,还有结点
  22. treenode=queuenode->data[queuenode->base];
  23. //printf("输出值:%c,%d\n",treenode->data,queuenode->base);
  24. queuenode->base=(queuenode->base+1)%MAXSIZE;//这里我们采用循环队列来实现存储,这样可以更有效的利用空间
  25. }
  26. return treenode;
  27. }
  28. void levelOrderOut(TreeNode *treeList){//层次遍历
  29. QueueNode *queuenode;
  30. TreeNode *p;
  31. queuenode=(QueueNode*)malloc(sizeof(QueueNode));
  32. queuenode->top=queuenode->base=0;
  33. if(treeList!=NULL)
  34. push1(queuenode,treeList);
  35. while(queuenode->top!=queuenode->base){
  36. p=pop1(queuenode);
  37. if(p->lchild!=NULL)//注意细节啊,在完整执行一次后,如果自己估算的没问题,就可能时变量什么的错了
  38. push1(queuenode,p->lchild);
  39. if(p->rchild!=NULL)
  40. push1(queuenode,p->rchild);
  41. }
  42. }

非递归的实现就稍微复杂一点了

先序非递归二叉树

非递归二叉树的遍历方式和递归二叉树的区别在于,我们需要保存其左右结点,以保证每个结点能正确被访问到,而递归层层嵌套,每一层执行完毕会回到上一层,所以我们可以不必保存每个结点。

我们可以使用栈来保存遍历的结点,栈的特点是后进先出,先保存当前根结点,然后输出,再保存右结点,栈顶位置加1,保存左节点,后进先出,所以先输出左节点,栈顶位置减1,然后再保存左节点的孩子节点,再输出右结点,保存右结点孩子结点,以此类推,直到二叉树遍历完毕。

  1. typedef struct StackNode{//栈存储结构
  2. TreeNode *top[MAXSIZE];
  3. int length;
  4. }StackNode;
  5. typedef struct TreeNode{//二叉树存储结构
  6. char data;
  7. struct TreeNode *lchild;
  8. struct TreeNode *rchild;
  9. }TreeNode;
  10. void push(StackNode *stacknode,TreeNode *treeList){//入栈方法
  11. if(treeList!=NULL){
  12. stacknode->top[stacknode->length]=treeList;
  13. stacknode->length++;
  14. }
  15. }
  16. TreeNode* pop(StackNode *stacknode){//出栈方法
  17. TreeNode *node;
  18. if(stacknode->length!=0)
  19. stacknode->length--;
  20. node=stacknode->top[stacknode->length];
  21. printf("出栈一个结点:%c\n",node->data);
  22. return node;
  23. }
  24. void PreOrderIn(TreeNode *treeList){//先序非递归遍历
  25. StackNode *stacknode;
  26. TreeNode *node;
  27. stacknode=(StackNode*)malloc(sizeof(StackNode));
  28. stacknode->length=0;
  29. if(treeList!=NULL){
  30. push(stacknode,treeList);
  31. while(stacknode->length !=NULL){
  32. node=pop(stacknode);
  33. if(node->rchild!=NULL){
  34. push(stacknode,node->rchild);
  35. }
  36. if(node->lchild!=NULL){
  37. push(stacknode,node->lchild);
  38. }
  39. }
  40. }
  41. }

中序非递归二叉树

中序非递归二叉树的特点是先入栈所有的左结点,然后再依次输出左节点,保存其右节点,然后再输出右结点,再以同样的方式遍历输出,直到整个二叉树遍历完毕。

  1. typedef struct StackNode{//栈存储结构
  2. TreeNode *top[MAXSIZE];
  3. int length;
  4. }StackNode;
  5. typedef struct TreeNode{//二叉树存储结构
  6. char data;
  7. struct TreeNode *lchild;
  8. struct TreeNode *rchild;
  9. }TreeNode;
  10. void push(StackNode *stacknode,TreeNode *treeList){//入栈方法
  11. if(treeList!=NULL){
  12. stacknode->top[stacknode->length]=treeList;
  13. stacknode->length++;
  14. }
  15. }
  16. TreeNode* pop(StackNode *stacknode){//出栈方法
  17. TreeNode *node;
  18. if(stacknode->length!=0)
  19. stacknode->length--;
  20. node=stacknode->top[stacknode->length];
  21. printf("出栈一个结点:%c\n",node->data);
  22. return node;
  23. }
  24. void InOrderIn(TreeNode *treeList){//中序非递归遍历
  25. StackNode *stacknode;
  26. TreeNode *p;
  27. TreeNode *node;
  28. p=treeList;
  29. stacknode=(StackNode*)malloc(sizeof(StackNode));
  30. stacknode->length=0;
  31. while(p!=NULL||stacknode->length!=0){//栈不为空或者树不为空则执行
  32. while(p!=NULL){
  33. push(stacknode,p);//让所有左孩子进栈
  34. p=p->lchild;
  35. }
  36. if(stacknode->length!=0){
  37. node=pop(stacknode);
  38. push(stacknode,node->rchild);//每个结点的右孩子进栈
  39. }
  40. }
  41. }

后序非递归遍历

后序非递归与前面不同的是,这里采用了do-while循环,由于我们的栈初始化是为空的,所以不能直接使用while循环,而是先执行一次再判断,后序遍历的规则是左结点,右节点,根节点,所以我们同样遍历所有左结点并保存,然后我们利用flag来表示我们当前在操作栈顶元素,如果当前flag为false,代表当前我们没有在操作栈顶元素,而是在操作未入栈的元素,因此我们将当前未入栈的右节点入栈,再重新按照一开始的遍历方式从栈顶元素开始操作,然后我们获取当前的栈顶元素,判断此时的结点的右孩子是否为空或者是已经遍历过的结点,我们用r保存,如果是,就更新当前变量r的结点,如果不是,就移动到右节点位置,保存右节点到栈中,再重新将所有变量置空,读取当前的右节点,并且出栈,并且将当前t置空,以保证每次操作的都是一个新的结点,而不是已经操作过的旧结点,就这样依次类推,完成遍历。

  1. void push(StackNode *stacknode,TreeNode *treeList){
  2. if(treeList!=NULL){
  3. stacknode->top[stacknode->length]=treeList;
  4. stacknode->length++;
  5. }
  6. }
  7. TreeNode* pop(StackNode *stacknode){
  8. TreeNode *node;
  9. if(stacknode->length!=0)
  10. stacknode->length--;
  11. node=stacknode->top[stacknode->length];
  12. printf("出栈一个结点:%c\n",node->data);
  13. return node;
  14. }
  15. TreeNode* GetTop(StackNode *stacknode,TreeNode *p){//获取栈顶结点
  16. if(p!=NULL){
  17. return p;
  18. }else{
  19. p=stacknode->top[stacknode->length-1];
  20. }
  21. return p;
  22. }
  23. void LastOrderIn(TreeNode *treeList){//后序非递归
  24. StackNode *stacknode;
  25. stacknode=(StackNode*)malloc(sizeof(StackNode));
  26. stacknode->length=0;
  27. TreeNode *p;
  28. TreeNode *t;
  29. TreeNode *r;
  30. bool flag;
  31. p=treeList;
  32. do{
  33. while(p!=NULL){
  34. push(stacknode,p);
  35. p=p->lchild;
  36. }
  37. r=NULL;
  38. t=NULL;
  39. flag=true;
  40. while(flag&&stacknode->length!=0){
  41. t=GetTop(stacknode,t);
  42. if(t->rchild==r){
  43. r=pop(stacknode);
  44. t=NULL;
  45. }else{
  46. t=t->rchild;
  47. push(stacknode,t);
  48. flag=false;
  49. }
  50. }
  51. } while(stacknode->length!=0);
  52. }

后序非递归这一块解释有一点绕可能(我讲的不够好,望各位见谅),如果各位看不懂或者还有什么不明白可以直接评论,我再给你们解释,这些代码都是我实战一遍之后才放出来给大家看的,所以执行上有什么问题可以找我。

虽然看起来复杂,但是我相信天下无难事,只怕有心人。加油!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/681822
推荐阅读
相关标签
  

闽ICP备14008679号