• 二叉树的递归遍历和非递归遍历


    目录

    一.二叉树的递归遍历

    1.先序遍历二叉树

    2.中序遍历二叉树

    3.后序遍历二叉树

    二.非递归遍历(栈)

    1.先序遍历

    2.中序遍历

    3.后序遍历


    一.二叉树的递归遍历

    定义二叉树

    1. #其中TElemType可以是int或者是char,根据要求自定
    2. typedef struct BiNode{
    3. TElemType data;
    4. struct BiNode, *lchild,*rchild;
    5. }BiNode,*BiTree;
    6. void visit(TElemType data) {
    7. printf("%d ", data);
    8. }
    1.先序遍历二叉树
    1. void PreOrderTraverse(BiTree T)
    2. {
    3. if(T==NULL)
    4. return ok;//空二叉树
    5. else
    6. {
    7. visit(T);//访问根节点
    8. PreOrderTraverse(T->lchild);//递归遍历左子树
    9. PreOrderTraverse(T->rchild);//递归遍历右子树
    10. }
    11. }
    2.中序遍历二叉树
    1. void InOrderTraverse(BiTree T)
    2. {
    3. if(T==NULL)
    4. return ok;//空二叉树
    5. else
    6. {
    7. InOrderTraverse(T->lchild);//递归遍历左子树
    8. visit(T);//访问根节点
    9. InOrderTraverse(T->rchild);//递归遍历右子树
    10. }
    11. }
    3.后序遍历二叉树
    1. void PostOrderTraverse(BiTree T)
    2. {
    3. if(T==NULL)
    4. return ok;//空二叉树
    5. else
    6. {
    7. PostOrderTraverse(T->lchild);//递归遍历左子树
    8. PostOrderTraverse(T->rchild);//递归遍历右子树
    9. visit(T);//访问根节点
    10. }
    11. }

    遍历的规则,以先序遍历为例:

    如果去掉输出语句,从递归角度,三种算法是完全相同的,三种算法访问路径是相同的,只是访问时机不同

    第一次经过时访问=先序遍历

    第二次经过时访问=中序遍历

    第三次经过时访问=后序遍历

    二.非递归遍历(栈)

    1. typedef struct BiTNode {
    2. char data;
    3. struct BiTNode *lchild, *rchild;
    4. } BiTNode, *BiTree;
    5. typedef struct {
    6. BiTree data[MAX_SIZE];
    7. int top;
    8. } Stack;
    9. void InitStack(Stack** S) {
    10. *S = (Stack*)malloc(sizeof(Stack));
    11. (*S)->top = -1;
    12. }
    13. int StackEmpty(Stack* S) {
    14. return (S->top == -1);
    15. }
    16. int StackFull(Stack* S) {
    17. return (S->top == MAX_SIZE - 1);
    18. }
    19. void push(Stack* S, BiTree elem) {
    20. if (StackFull(S)) {
    21. printf("Stack is full. Cannot push element.\n");
    22. return;
    23. }
    24. S->top++;
    25. S->data[S->top] = elem;
    26. }
    27. void pop(Stack* S, BiTree* elem) {
    28. if (StackEmpty(S)) {
    29. printf("Stack is empty. Cannot pop element.\n");
    30. return;
    31. }
    32. *elem = S->data[S->top];
    33. S->top--;
    34. }
    35. int GetTop(Stack* S, BiTree* elem) {
    36. if (StackEmpty(S)) {
    37. printf("Stack is empty. Cannot get top element.\n");
    38. return 0;
    39. }
    40. *elem = S->data[S->top];
    41. return 1;
    42. }
    1.先序遍历

    (1)从根结点开始先压左路结点,并访问结点,直到把根结点和左路结点全部压入栈。

    (2)若左子树和为空,说明左路和根结点已经全部压栈并且已经访问过了,开始取栈顶元素来访问上一层父节点的右子树。把右子树看成子问题继续进行(1)

    (3)依次进行上述(1)和(2)压栈访问和出栈操作,直到栈为空或者右子树为空这说明遍历完毕

    1. void PreOrderTraverse(BiTree T)
    2. {
    3. Stack* S;
    4. BiTree p, q;
    5. InitStack(&S);
    6. p = T;
    7. while (p || !StackEmpty(S))
    8. {
    9. if (p)
    10. {
    11. printf("%c", p->data);
    12. push(S, p); // 将节点 p 入栈
    13. p = p->lchild;
    14. }
    15. else
    16. {
    17. pop(S, &q);
    18. p = q->rchild;
    19. }
    20. }
    21. free(S); // 释放 Stack 结构体内存
    22. }
    2.中序遍历

    中序遍历和先序遍历思路类似,区别在于,先序遍历先访问根,在访问左,中序遍历先访问左,在访问根,最后都再访问右

    1. void InOrderTraverse(BiTree T)
    2. {
    3. Stack* S;
    4. BiTree p, q;
    5. InitStack(&S);
    6. p = T;
    7. while (p || !StackEmpty(S))
    8. {
    9. if (p)
    10. {
    11. push(S, p);
    12. p = p->lchild;
    13. }
    14. else
    15. {
    16. pop(S, &q);
    17. printf("%c", q->data);
    18. p = q->rchild;
    19. }
    20. }
    21. free(S); // 释放 Stack 结构体内存
    22. }
    3.后序遍历

    后续遍历必须访问完左右子树之后才可以访问父亲结点,所以访问完左子树时,现在得去访问右子树,通过栈找到父亲结点(这是第一次top父亲结点),然后找到父亲结点的右子树进行访问,当把右子树访问完成后,再通过栈找到父亲结点进行访问(这是第二次top父亲结点A)。那么怎么才知道这时是第一次top和第二次top呢?如果不做处理的话这里就会一直认为是第一次top父亲节点,不出栈,就会造成死循环,所以怎样解决呢?

    这里创建一个prev结点,用来记录上一次出栈的结点,若上一次出栈的结点为右子树,这说明可以访问根结点了,说明是已经第二次top父亲结点

    1. void PostOrderTraverse(BiTree T)
    2. {
    3. Stack* S;
    4. BiTree p = T;
    5. InitStack(&S);
    6. BiTree prev = NULL;
    7. while (p || !StackEmpty(S))
    8. {
    9. while (p)
    10. {
    11. push(S, p);
    12. p = p->lchild;
    13. }
    14. BiTree q;
    15. if (GetTop(S, &q) && (q->rchild == NULL || q->rchild == prev))
    16. {
    17. printf("%c ", q->data);
    18. prev = q;//更新q结点
    19. pop(S, &p);
    20. p = NULL;
    21. }
    22. else if (q->rchild != NULL)
    23. {
    24. p = q->rchild;
    25. }
    26. }
    27. free(S);
    28. }
  • 相关阅读:
    产品发布+联合演讲+认证+奖项丨云和恩墨在openGauss Developer Day 2023主论坛大放异彩...
    小红书怎么涨粉?想要涨粉要注意以下五点
    ARM 详解
    解决Redis、MySQL缓存双写不一致问题
    滑动窗口最大值问题
    Java市场真的饱和了吗?
    【HDU No. 3567】八数码 II Eight II
    如何升级到 Docker Compose v2
    嵌入式开发:创建和使用可移植类型的7个技巧
    素数筛法代码-总结(Python,C++)
  • 原文地址:https://blog.csdn.net/weixin_69884785/article/details/132605118