• 数据结构初阶 · 链式二叉树的部分问题


    目录

    前言:

    1 链式二叉树的创建

    2 前序 中序 后序遍历

    3 树的节点个数

    4 树的高度

    5 树的叶子节点个数

    6 树的第K层节点个数


    前言:

    链式二叉树我们在C语言阶段已经实现了,这里介绍的是涉及到的部分问题,比如求树的高度,求树的节点个数等,连接部分就手动连接,用一个样例来介绍涉及到的几个问题。

    这里对前面知识反馈比较大的是递归,可以说每个问题都用到了递归。


    1 链式二叉树的创建

    因为是链式二叉树,所以有两个指针,分别指向右孩子节点和左孩子节点,给上值,手动连接即可:

    1. typedef struct TreeNode
    2. {
    3. struct TreeNode* left = NULL;
    4. struct TreeNode* right = NULL;
    5. int data;
    6. }TreeNode;
    7. TreeNode* Tree()
    8. {
    9. TreeNode* node1 = new TreeNode;
    10. TreeNode* node2 = new TreeNode;
    11. TreeNode* node3 = new TreeNode;
    12. TreeNode* node4 = new TreeNode;
    13. TreeNode* node5 = new TreeNode;
    14. TreeNode* node6 = new TreeNode;
    15. //TreeNode* node7 = new TreeNode;
    16. node1->data = 1;
    17. node2->data = 2;
    18. node3->data = 3;
    19. node4->data = 4;
    20. node5->data = 5;
    21. node6->data = 6;
    22. //node7->data = 7;
    23. node1->left = node2;
    24. node1->right = node4;
    25. node2->left = node3;
    26. node4->left = node5;
    27. node4->right = node6;
    28. //node5->left = node7;
    29. return node1;
    30. }

    整体构建出来就是这样,对于初学链式二叉树的同学来说,NULL给上是很有必要的,这对于后面的遍历问题有帮助。


    2 前序 中序 后序遍历

    前序遍历的顺序是 根 左子树 右子树,中序遍历的的顺序是左子树 根 右子树 ,后序遍历的顺序是左子树,右子树,根。

    咱们从前序开始,顺便进行打印,从1开始,到左子树是2,2的左子树是3,3是左子树是NULL,再往下就没了,递归回来是3的右子树,那么此时2的左子树就遍历完成了,2的右子树是NULL,这时候1的左子树就遍历完了,然后是右子树,到4,然后是4的左子树5,接着是5的左右子树,这时4的左子树就遍历完了,然后是右子树,当6遍历完了之后,才是整个树都遍历完了。

    那么代码会不会很复杂呢?

    不会,递归都有一个特点,思路难想,代码简单:

    1. void PrevOrder(TreeNode* root)
    2. {
    3. if (root == NULL)
    4. {
    5. cout << "N ";
    6. return;
    7. }
    8. cout << root->data << " ";
    9. PrevOrder(root->left);
    10. PrevOrder(root->right);
    11. }

    这就遍历完了。

    打印的结果是1 2 3 N N N 4 5 N N 6 N N,那么为了加强理解我们可以这样看:

    3 N N N是2的左右子树,5 N N 6 N N是4的左右子树,2 4 是1 的左右子树,这样结合起来就好理解多了。

    那么对于中序后序来说都是一样的,这里给代码,就不重复演示了:

    1. void InOrder(TreeNode* root)
    2. {
    3. if (root == NULL)
    4. {
    5. cout << "N ";
    6. return;
    7. }
    8. InOrder(root->left);
    9. cout << root->data << " ";
    10. InOrder(root->right);
    11. }
    12. void BackOrder(TreeNode* root)
    13. {
    14. if (root == NULL)
    15. {
    16. cout << "N ";
    17. return;
    18. }
    19. BackOrder(root->left);
    20. BackOrder(root->right);
    21. cout << root->data << " ";
    22. }

    3 树的节点个数

    树的节点个数问题,使用的是分而治之的思想,比如一个院,要统计有多少人,那么院长就发号司令,副院长去问班主任,班主任去问辅导员,辅导员去问班长,然后加上自己,最后就可以得到总总共的人数。

    树的节点个数是一样的,求总节点个数,我们可以把树分为左右子树,把一个树拆分成无数的左右子树,统计每个左右子树的节点个数,相加即可。

    代码实现:

    1. size_t TreeSize(TreeNode* root)
    2. {
    3. return root == NULL? 0 :
    4. TreeSize(root->left) + TreeSize(root->right) + 1;
    5. }

    是的,代码就这么简单,这里给上递归展示图看看:

    这里递归的是1的右子树。


    4 树的高度

    树的高度同理,我们可以理解为两个院的人比最高的,树的高度即我们同理,返回左右子树高度最高的即可,因为一个节点本身就算1,所以返回高度的时候需要加1,返回的条件就是节点为空,为空就返回0:

    1. size_t TreeHeightError(TreeNode* root)
    2. {
    3. return root == NULL ? 0 :
    4. TreeHeight(root->left) > TreeHeight(root->right) ?
    5. TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
    6. }

    以上代码看起来是没问题的,100个样例能跑过90的样例,但是数据量一多就会崩盘,因为这里没有记录左右子树的高度,也就是说会重复计算,这里不要小看重复计算,没有记录那么所有的计算都会翻二倍,而每一层都没有记录,所有越往层数多的走,就会变成2^n的重复计算,是指数量级的增长,所以我们应该记录数据:

    1. size_t TreeHeight(TreeNode* root)
    2. {
    3. if (root == NULL) {
    4. return 0;
    5. }
    6. size_t leftHeight = TreeHeight(root->left);
    7. size_t rightHeight = TreeHeight(root->right);
    8. return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    9. }

    5 树的叶子节点个数

    叶子节点即没有孩子节点的孩子,那么返回1的条件就是左右孩子都为空,如果不为空往下遍历就ok了:

    1. size_t TreeLeaf(TreeNode* root)
    2. {
    3. if (root == NULL)
    4. {
    5. return 0;
    6. }
    7. if (root->left == NULL && root->right == NULL)
    8. {
    9. return 1;
    10. }
    11. return TreeLeaf(root->left) + TreeLeaf(root->right);
    12. }

    6 树的第K层节点个数

    这是本文最难的一个问题了,该问题的子问题是:

    空节点的时候返回0,k = 1的时候返回1,K > 1的时候遍历即可,个人认为k - 1是这个问题的关键所在:

    1. size_t TreeNodeK(TreeNode* root,size_t k)
    2. {
    3. if (root == NULL)
    4. {
    5. return 0;
    6. }
    7. if (k == 1)
    8. {
    9. return 1;
    10. }
    11. return TreeNodeK(root->left, k - 1) +
    12. TreeNodeK(root->right, k - 1);
    13. }

    因为遍历是从根节点开始遍历的,所以减到k = 1的时候,是涉及到的那层,对该层进行判断,这是存在一个顺序问题,root == NULL应该在k == 1之前,因为递归到那一层,k = 1是铁定的,这时候就需要先判断是不是空节点,不是空节点再讨论K= 1的问题。

    以上所有问题的测试代码:

    1. int main()
    2. {
    3. TreeNode* node = Tree();
    4. size_t k = 2;
    5. //前序遍历
    6. PrevOrder(node);
    7. cout << endl;
    8. //中序遍历
    9. InOrder(node);
    10. cout << endl;
    11. //后序遍历
    12. BackOrder(node);
    13. cout << endl;
    14. //树的节点个数
    15. cout << "The Tree's size is:" <<
    16. TreeSize(node) << endl;
    17. //树的高度
    18. cout << "The Tree's Height is:" <<
    19. TreeHeightError(node) << endl;
    20. //树的叶子节点个数
    21. cout << "The Tree's leaf is:" <<
    22. TreeLeaf(node) << endl;
    23. //树的第K层节点个数
    24. cout << "The Tree's leaf about K is:" <<
    25. TreeNodeK(node,k) << endl;
    26. return 0;
    27. }

    感谢阅读!

  • 相关阅读:
    微服务架构--介绍
    高性能MySQL实战(三):性能优化 | 京东物流技术团队
    记一次由JWT导致的绕过权限
    前端面试题目(二十三)
    Django之VScode工程搭建
    使用群晖实现Videostation电影的大容量存储及分享教程
    【入门】二分查找右侧边界
    Android CameraX 仿一甜相机(录像、拍照、可调节尺寸、聚焦、照明、网格线),最全的CameraX教程
    01--MySQL数据库概述
    游戏报错d3dcompiler_47.dll缺失怎么修复,总结多种修复方法
  • 原文地址:https://blog.csdn.net/2301_79697943/article/details/139578366