本部分中的非递归方式的3种遍历方式相对复杂。

非递归 先序 遍历

Traverse 美 [trəˈvɜrs] n.穿过; 横贯,横切; 横木; [建]横梁 vt.横越,横贯; 通过; [法]否认,反驳; [木工]横刨 vi.来回移动; 旋转; 横越; adj.横贯的; 横切的;

  1. //b: 指向二叉树的根节点
  2. void PreOrderTraverse(BiTree b){
  3. //建栈
  4. InitStack(S);
  5. //设置临时的辅助指针,功能: 遍历结点,初值为指向根节点
  6. BiTree p = b;
  7. //p指向的结点不为空 || 栈不为空
  8. while(p || !IsEmpty(S)){
  9. //1. 从根结点走到左下角最下面的结点:每个结点,先访问(E.g 打印该结点数据),再压栈;
  10. //简记:只要左子树存在,就一直循环下去(访问+压栈),否则跳出当前循环;
  11. while(p){
  12. printf("%c",p->data); //先访问:先序先遍历当前p指向的结点
  13. Push(S,p); //压栈:将指向当前结点的指针(简记:将当前结点压栈),先压进栈保存
  14. p=p->lchild; //让当前指针指向左孩子;
  15. }
  16. //2. 先将最左下角结点出栈(即:top保存的结点出栈),然后访问它的右子树,直到栈为空;
  17. //先判断当前栈是否有数据,如果有则出栈;
  18. if(!IsEmpty(S)){
  19. //让临时指针指向当前栈顶元素出栈,让p指向栈顶对应的结点:
  20. p = Pop(S);
  21. //然后让临时指针再指向当前结点的右孩子(右子树):
  22. p = p->rchild;
  23. }
  24. }
  25. }

4.3 - 图1

非递归 中序 遍历
  1. void MidOrderTraverse(BiTree b){
  2. //建栈
  3. InitStack(S);
  4. //设置临时的辅助指针,功能: 遍历结点,初值为指向根节点
  5. BiTree p = b;
  6. //p指向的结点不为空 || 栈不为空
  7. while(p || !IsEmpty(S)){
  8. //1. 从根结点走到左下角最下面的结点:每个结点,先访问(E.g 打印该结点数据),再压栈;
  9. //简记:只要左子树存在,就一直循环下去(访问+压栈),否则跳出当前循环;
  10. while(p){
  11. //printf("%c",p->data); //先访问:先序先遍历当前p指向的结点
  12. Push(S,p); //压栈:将指向当前结点的指针(简记:将当前结点压栈),先压进栈保存
  13. p=p->lchild; //让当前指针指向左孩子;
  14. }
  15. //2. 先将最左下角结点出栈(即:top保存的结点出栈),然后访问它的右子树,直到栈为空;
  16. //先判断当前栈是否有数据,如果有则出栈;
  17. if(!IsEmpty(S)){
  18. //让临时指针指向当前栈顶元素出栈,让p指向栈顶对应的结点:
  19. p = Pop(S);
  20. //把访问放到这里:
  21. printf("%c",p->data); //访问:先序先遍历当前p指向的结点
  22. //然后让临时指针再指向当前结点的右孩子(右子树):
  23. p = p->rchild;
  24. }
  25. }
  26. }

4.3 - 图2

非递归 后续 遍历
  1. void PostOrderTraverse(BiTree b)
  2. {
  3. //建栈
  4. InitStack(S);
  5. //设置临时的辅助指针,功能: 遍历结点,初值为指向根节点
  6. BiTree p = b;
  7. //用于记录刚刚访问过的右子树的父结点:
  8. BiTree r = NULL;
  9. //p指向的结点不为空 || 栈不为空
  10. while(p || !IsEmpty(S))
  11. {
  12. //1. 从根结点到最左下角的左子树都入栈;
  13. //因为外循环while()已经包含了p的情况,所以这里可以直接判断:
  14. if(p)
  15. {
  16. //压栈:将指向当前结点的指针(简记:将当前结点压栈),先压进栈保存
  17. Push(S,p);
  18. //让当前指针指向左孩子;
  19. p=p->lchild;
  20. //然后返回到外循环while(),再继续判断p左孩子是否存在,如果存在则继续压栈;
  21. }
  22. //2. 返回栈顶的2种情况,需要分别处理:
  23. else
  24. {
  25. //只取栈顶的元素,先不出栈。相当于先试读一下栈顶的元素(让p指向栈顶对应的结点):
  26. GetTop(S,p);
  27. //2.1 如果从左子树返回父结点,右子树还没有访问,则让p指向当前结点的右子树:
  28. //p指向的当前结点的右子树存在 && 相邻两次访问的结点是右结点
  29. if(p->rchild && p->rchild != r)
  30. {
  31. p = p->rchild;
  32. }
  33. //2.2 如果从右子树返回其父节点,则出栈,
  34. else
  35. {
  36. //让临时指针指向当前栈顶元素出栈,让p指向栈顶对应的结点://不让p指向该栈顶结点元素,下面一句真么访问打印其内容呢?呵呵,所以要出栈。
  37. Pop(S,p);
  38. //然就访问该结点:
  39. printf("%c",p->data);
  40. //让r指向p,进行备份p的值,目的是让下次比较其是否相等,如果相等说明已经访问了右子树,如果不相等则说明还没有访问右子树:
  41. r = p;
  42. //置空的目的:保证从左侧返回到根结点或某个结点的左侧子树全部访问完,使其可以访问对应的右子树,所以这里将p置空,直接跳过上面if(p)的判断,直接去执行栈顶元素的右子树。
  43. p = NULL;
  44. }
  45. }
  46. }
  47. }

4.3 - 图3

线索 二叉树 遍历
  1. //中序遍历线索二叉树
  2. void InOrderTraverse(ThreadTree T)
  3. {
  4. //临时指针p指向传递过来的二叉树的根结点:
  5. ThreadTree p = T;
  6. while(p)
  7. {
  8. //1. 找最左孩子
  9. //如果左标志位为0,即p当前指向的结点,其左孩子存在的话,则一直循环,找到最左下角结点:
  10. while(p->Itag == 0)
  11. {
  12. //再找其左孩子:
  13. p = p->lchild;
  14. }
  15. //2. 根据中序遍历的要求,访问结点
  16. //访问: E.g 这里的打印该结点的数据内容
  17. printf("%c", p->data);
  18. //3. 再访问它的右子树:
  19. //p当前指向的结点没有右孩子(即 p->rtag == 1,表示存在后继) && 后继指针指向的结点存在(因为最后右侧的后继指针=NULL,为了结束外循环)
  20. while(p->rtag == 1 && p->rchild)
  21. {
  22. //让p指向其对应的右结点:
  23. p = p->rchild;
  24. //访问该右结点:
  25. printf("%c", p->data);
  26. }
  27. //当上面一个while循环的p->tag=0时且其右孩子时(E.g B结点),那么下面的一句让其指向其右孩子结点:(避免漏掉此情况);
  28. //当指向C结点时其右孩子指向为NULL,同时即可跳出当前所在的外循环:
  29. p->rchild;
  30. }
  31. }

4.3 - 图4