• 链式二叉树的实现及遍历(C语言版)


    目录

    1 基本概念

    1.1 树的概念

    1.2 二叉树的链式表示

    1.2.1 "左孩子右兄弟"表示法

    1.2.2 "左右子树"表示法

    1.2.3 手动构建一棵树

    2 树的遍历

    2.1 前序遍历/先序遍历

    2.2 中序遍历

    2.3 后序遍历

    2.4 层序遍历

    2.4.1 算法思想

    ​编辑 2.4.2 带头尾指针链式队列的代码

    3  其他接口函数

    3.1 求树的节点个数

    3.2 求叶子节点个数

    3.3 二叉树的销毁

    3.4  遍历寻找二叉树中值为x的节点 


    1 基本概念


    1.1 树的概念

    ⭕树是一种非线性的数据结构

    ⭕树的根结点没有前驱节点,根节点可以指向任意多个子节点(N叉树)

    ⭕树形结构中,子树之间不能有交集,否则就是图


    ⭕度:一个节点含有的子树的个数。例如二叉树的根节点的度为2,上图A节点的度为3

    ⭕树的度:一棵树中最大的节点的度。如二叉树的度就是其根节点的度,上图树的度为3

    ⭕树的高度或深度:树中节点的最大层次。如上图树的高度为3

    ⭕叶子节点或终端节点:度为0的节点。如上图的E F G均为叶子节点


    1.2 二叉树的链式表示


    1.2.1 "左孩子右兄弟"表示法


    1.2.2 "左右子树"表示法


    1.2.3 手动构建一棵树


    1. typedef char BTDataType;
    2. typedef struct BinaryTreeNode
    3. {
    4. struct BinaryTreeNode* left;
    5. struct BinaryTreeNode* right;
    6. BTDataType data;
    7. }BTNode;
    8. BTNode* A = (BTNode*)malloc(sizeof(BTNode));
    9. A->data = 'A';
    10. A->left = NULL;
    11. A->right = NULL;
    12. BTNode* B = (BTNode*)malloc(sizeof(BTNode));
    13. B->data = 'B';
    14. B->left = NULL;
    15. B->right = NULL;
    16. BTNode* C = (BTNode*)malloc(sizeof(BTNode));
    17. C->data = 'C';
    18. C->left = NULL;
    19. C->right = NULL;
    20. BTNode* D = (BTNode*)malloc(sizeof(BTNode));
    21. D->data = 'D';
    22. D->left = NULL;
    23. D->right = NULL;
    24. BTNode* E = (BTNode*)malloc(sizeof(BTNode));
    25. E->data = 'E';
    26. E->left = NULL;
    27. E->right = NULL;
    28. A->left = B, A->right = C;
    29. B->left = D, B->right = E;

    2 树的遍历


    2.1 前序遍历/先序遍历

    🥝前序遍历/先序遍历:又叫深度优先遍历,根->左子树->右子树

    问:下图的树先序遍历的输出结果是什么?


    很多教材上的答案是ABDEC,但其实对于初学者特别不友好,初学者可能看得懂这个答案,但是到中序和后序遍历就看不懂了,所以我复现一下遍历过程:

    所以教材上的答案多半忽略了对空指针的访问输出,这其实对我们理解遍历是不利的。

    上面这个动图是我自己手动制作的,如果想要自己也动起手来,可以访问下面这篇我的博客:

    ScreenToGif-动图制作软件实用操作-CSDN博客


    1. void PrevOrder(BTNode* root)
    2. {
    3. if (root == NULL)
    4. {
    5. printf("(null) ");
    6. return;
    7. }
    8. printf("%c ", root->data);
    9. PrevOrder(root->left);
    10. PrevOrder(root->right);
    11. }


    2.2 中序遍历

    🍋中序遍历:左子树->根->右子树

     问:下图的树中序遍历的输出结果是什么?


    建议大家花个几分钟时间自己做一下,空指针访问也表示出来,有利于帮助我们理解递归。

    1. void InOrder(BTNode* root)
    2. {
    3. if (root == NULL)
    4. {
    5. printf("(null) ");
    6. return;
    7. }
    8. InOrder(root->left);
    9. printf("%c ", root->data);
    10. InOrder(root->right);
    11. }

    答案:

     


    2.3 后序遍历

    🍇后序遍历:左子树->右子树->根

    问:下图的树中序遍历的输出结果是什么?


    动图就不制作了,大家可以验证答案后自己动手制作动图。

    1. void PostOrder(BTNode* root)
    2. {
    3. if (root == NULL)
    4. {
    5. printf("(null) ");
    6. return;
    7. }
    8. PostOrder(root->left);
    9. PostOrder(root->right);
    10. printf("%c ", root->data);
    11. }


    2.4 层序遍历

    🍍层序遍历:一层一层节点遍历,又叫广度优先遍历

    层序遍历本身直观,实现起来比较麻烦,思想蕴含在代码里。

    2.4.1 算法思想

    ①利用先进先出队列,第一次先入二叉树的根节点到队列中,然后入不为空的子节点,pop掉根节点,如果树的根节点都为空,那么就没有入的必要了。

    1. if (root)
    2. QuePush(&q, root);
    3. if(root->left)
    4. QuePush(&q,root->left);
    5. if(root->right)
    6. QuePush(&q,root->right);
    7. QuePop(&q);

    ②第二次入原来根节点的左子树根节点的左右非空节点,然后pop掉该节点。

    ③第三次入原来根节点的右子树根节点的左右非空节点,然后pop掉该节点。

    ④依次循环,直到队列为空。那么只要队列不为空,循环就继续。

    1. void LevelOrder(BTNode* root)
    2. {
    3. Que q;
    4. QueInit(&q);
    5. if(root)
    6. QuePush(&q,root);
    7. while (!QueEmpty(&q))
    8. {
    9. BTNode* front = QueFront(&q);
    10. QuePop(&q);
    11. printf("%c ", front->data);
    12. if (front->left)
    13. QuePush(&q, front->left);
    14. if (front->right)
    15. QuePush(&q, front->right);
    16. }
    17. }

     2.4.2 带头尾指针链式队列的代码

    Queue.h

    1. #pragma once
    2. #define _CRT_SECURE_NO_WARNINGS 1
    3. #include
    4. #include
    5. #include
    6. #include
    7. struct BinaryTreeNode;
    8. typedef struct BinaryTreeNode* QDataType;
    9. typedef struct QueueNode
    10. {
    11. struct QueueNode* next;
    12. QDataType data;
    13. }QNode;
    14. typedef struct Queue
    15. {
    16. QNode* head;
    17. QNode* tail;
    18. int size;
    19. }Que;
    20. void QueInit(Que* pq);
    21. void QueDestroy(Que* pq);
    22. void QuePush(Que* pq, QDataType x);
    23. void QuePop(Que* pq);
    24. QDataType QueFront(Que* pq);
    25. QDataType QueBack(Que* pq);
    26. bool QueEmpty(Que* pq);
    27. int QueSize(Que* pq);

    Queue.c

    1. #include"Queue.h"
    2. void QueInit(Que* pq)
    3. {
    4. assert(pq);
    5. pq->head = pq->tail = NULL;
    6. pq->size = 0;
    7. }
    8. void QueDestroy(Que* pq)
    9. {
    10. assert(pq);
    11. QNode* cur = pq->head;
    12. while (cur)
    13. {
    14. QNode* next = cur->next;
    15. free(cur);
    16. cur = next;
    17. }
    18. pq->head = pq->tail = NULL;
    19. }
    20. void QuePush(Que* pq, QDataType x)
    21. {
    22. assert(pq);
    23. QNode* newnode = (QNode*)malloc(sizeof(QNode));
    24. newnode->next = NULL;
    25. newnode->data = x;
    26. if (newnode == NULL)
    27. {
    28. perror("malloc fail\n");
    29. exit(-1);
    30. }
    31. if (pq->head == NULL)
    32. {
    33. pq->head = pq->tail = newnode;
    34. }
    35. else
    36. {
    37. pq->tail->next = newnode;
    38. pq->tail = newnode;
    39. }
    40. pq->size++;
    41. }
    42. void QuePop(Que* pq)
    43. {
    44. assert(pq);
    45. assert(!QueEmpty(pq));
    46. //单结点
    47. if (pq->head->next == NULL)
    48. {
    49. free(pq->head);
    50. pq->tail = pq->head = NULL;
    51. }
    52. else
    53. {
    54. QNode* next = pq->head->next;
    55. free(pq->head);
    56. pq->head = next;
    57. }
    58. pq->size--;
    59. }
    60. QDataType QueFront(Que* pq)
    61. {
    62. assert(pq);
    63. assert(!QueEmpty(pq));
    64. return pq->head->data;
    65. }
    66. QDataType QueBack(Que* pq)
    67. {
    68. assert(pq);
    69. assert(!QueEmpty(pq));
    70. return pq->tail->data;
    71. }
    72. bool QueEmpty(Que* pq)
    73. {
    74. assert(pq);
    75. return pq->head == NULL;
    76. }
    77. int QueSize(Que* pq)
    78. {
    79. assert(pq);
    80. return pq->size;
    81. }

    3  其他接口函数


    3.1 求树的节点个数

    算法思想:

    ①左子树的节点个数+根本身+右子树的节点个数

    ②根为空就返回

    1. int TreeSize(BTNode* root)
    2. {
    3. if(root==NULL)
    4. return 0;
    5. return 1+TreeSize(root->left)+TreeSize(root->right);
    6. }

    3.2 求叶子节点个数

    算法思想:

    ①什么是叶子:度为0,即左指针和右指针为空

    ②遇到空则返回

    ③一棵树的叶子节点=左子树的叶子节点+右子树的叶子节点

    1. int TreeLeafSize(BTNode* root)
    2. {
    3. if (root == NULL)
    4. return 0;
    5. if (root->left == NULL && root->right == NULL)
    6. return 1;
    7. return TreeLeafSize(root->left) + TreeLeafSize(root->right);
    8. }

    3.3 二叉树的销毁

    问:为什么选择后序遍历销毁二叉树?而不是前序和中序遍历?

    答:前序遍历会导致访问不到根左子树和右子树,引发对空指针的访问;中序遍历会导致访问不到根的右子树,引发对空指针的访问;只有后序遍历才能保证销毁根的左子树和右子树后再销毁根。

    1. void TreeDestroy(BTNode** proot)
    2. {
    3. if (*(proot) == NULL)
    4. return;
    5. TreeDestroy((*proot)->left);
    6. TreeDestroy((*proot)->right);
    7. free(*proot);
    8. *proot=NULL;
    9. }

    3.4  遍历寻找二叉树中值为x的节点 

    ①采用先序的思想,即根->左子树->右子树访问

    ②先判断根是否是目标节点,不是就递归左子树和右子树

    ③如果左子树中找到了就直接返回,如果左子树没找到就去右子树找

    ④左右子树都找不到说明就找不到了

    1. BTNode* TreeFind(BTNode* root, BTDataType x)
    2. {
    3. if (root == NULL)
    4. return NULL;
    5. if (root->data == x)
    6. return root;
    7. BTNode* ret=TreeFind(root->left, x);
    8. if (ret != NULL)
    9. return ret;
    10. ret=TreeFind(root->right, x);
    11. if (ret != NULL)
    12. return ret;
    13. return NULL;
    14. }

     4 二叉树代码

    BinaryTree.h

    1. #pragma once
    2. #define _CRT_SECURE_NO_WARNINGS 1
    3. #include
    4. #include
    5. #include
    6. typedef char BTDataType;
    7. typedef struct BinaryTreeNode
    8. {
    9. struct BinaryTreeNode* left;
    10. struct BinaryTreeNode* right;
    11. BTDataType data;
    12. }BTNode;
    13. BTNode* BuyNode(BTDataType x);
    14. //前序遍历:根 左子树 右子树
    15. void PrevOrder(BTNode* root);
    16. //中序遍历:左子树 根 右子树
    17. void InOrder(BTNode* root);
    18. //后序遍历:左子树 右子树 根
    19. void PostOrder(BTNode* root);
    20. int TreeSize(BTNode* root);
    21. int TreeLeafSize(BTNode* root);
    22. void TreeDestroy(BTNode** proot);
    23. BTNode* TreeFind(BTNode* root, BTDataType x);
    24. //层序遍历
    25. void LevelOrder(BTNode* root);

     BinaryTree.c

    1. #include"BinaryTree.h"
    2. #include"Queue.h"
    3. BTNode* BuyNode(BTDataType x)
    4. {
    5. BTNode* root = (BTNode*)malloc(sizeof(BTNode));
    6. root->data = x;
    7. root->left = root->right = NULL;
    8. return root;
    9. }
    10. //前序遍历:根 左子树 右子树
    11. void PrevOrder(BTNode* root)
    12. {
    13. if (root == NULL)
    14. {
    15. printf("(null) ");
    16. return;
    17. }
    18. printf("%c ", root->data);
    19. PrevOrder(root->left);
    20. PrevOrder(root->right);
    21. }
    22. //中序遍历:左子树 根 右子树
    23. void InOrder(BTNode* root)
    24. {
    25. if (root == NULL)
    26. {
    27. printf("(null) ");
    28. return;
    29. }
    30. InOrder(root->left);
    31. printf("%c ", root->data);
    32. InOrder(root->right);
    33. }
    34. //后序遍历:左子树 右子树 根
    35. void PostOrder(BTNode* root)
    36. {
    37. if (root == NULL)
    38. {
    39. printf("(null) ");
    40. return;
    41. }
    42. PostOrder(root->left);
    43. PostOrder(root->right);
    44. printf("%c ", root->data);
    45. }
    46. int TreeSize(BTNode* root)
    47. {
    48. return root == NULL ? 0 : 1 + TreeSize2(root->left) + TreeSize2(root->right);
    49. }
    50. int TreeLeafSize(BTNode* root)
    51. {
    52. if (root == NULL)
    53. return 0;
    54. if (root->left == NULL && root->right == NULL)
    55. return 1;
    56. return TreeLeafSize(root->left) + TreeLeafSize(root->right);
    57. }
    58. //层序遍历
    59. void LevelOrder(BTNode* root)
    60. {
    61. Que q;
    62. QueInit(&q);
    63. if(root)
    64. QuePush(&q,root);//队列每一个元素的类型都是BTNode*
    65. while (!QueEmpty(&q))
    66. {
    67. BTNode* front = QueFront(&q);
    68. QuePop(&q);
    69. printf("%c ", front->data);
    70. if (front->left)
    71. QuePush(&q, front->left);
    72. if (front->right)
    73. QuePush(&q, front->right);
    74. }
    75. }
    76. void TreeDestroy(BTNode** proot)
    77. {
    78. if (*proot == NULL)
    79. return;
    80. TreeDestroy((*proot)->left);
    81. TreeDestroy((*proot)->right);
    82. free((*proot));
    83. *proot = NULL;
    84. }
    85. BTNode* TreeFind(BTNode* root, BTDataType x)
    86. {
    87. if (root == NULL)
    88. return NULL;
    89. if (root->data == x)
    90. return root;
    91. BTNode* ret=TreeFind(root->left, x);
    92. if (ret != NULL)
    93. return ret;
    94. ret=TreeFind(root->right, x);
    95. if (ret != NULL)
    96. return ret;
    97. return NULL;
    98. }

  • 相关阅读:
    华硕平板k013me176cx线刷方法
    LAXCUS分布式操作系统6.0 RP1版本正式发布
    Goland运行go语言基础篇
    AI系统源码ChatGPT网站源码+ai绘画系统/支持GPT4.0/支持Midjourney局部编辑重绘
    Toronto Research Chemicals altronojirimycin 盐酸盐研究工具
    使用 Jetpack Compose 实现一个计算器APP
    Roaring Bitmap 更好的位图压缩算法
    ASPICE标准快速掌握「2.2. 过程参考模型(Process Reference Model,PRM)」
    Vue3中的常用组件通信大总结 包括最Vue3.4defineModel()实现组件双向绑定
    点云绪论(点云数据及获取、点云数据处理、常用软件及开源库)
  • 原文地址:https://blog.csdn.net/weixin_73053512/article/details/133175678