• 二叉树的Morris遍历


    不同于通过递归实现的中序遍历,二叉树还存在一种通过循环实现的Morris遍历,,这种遍历方式从结果上来看与中序遍历相同,而由于是用循环代替了递归,空间复杂度会从O(H)(H为二叉树的高度)变为O(1)

    线索二叉树

    二叉树的Morris遍历,是基于线索二叉树来做的

    而线索二叉树,简单来讲,就是将左节点不为空的节点的左子树的最右节点的右节点指向该节点

    例如这样一个二叉树

    其中 4 的左节点为 1 ,而 1 的最右节点为 3 ,因此我们需要将3的右节点指向 1 (虚线表示)

     

     因此,将该树表示为线索二叉树为

     


    Morris遍历

    在中序遍历中,我们的遍历顺序为左节点 - 根节点 - 右节点

    首先,想要得到左节点,我们可以直接得到(root=root->left)

    而在遍历到左节点后,重点就是如何重新得到根节点,线索二叉树就能够很好的解决这个问题,在遍历完左子树之后,我们一定会处于左子树的最右节点,这时我们就可以通过线索二叉树来重新回到根节点

    因此,转换为代码,我们只需要在遍历每个节点时判断是否存在左子树,若存在左子树,则去构建该位置的线索二叉树,并向左遍历, 而若是不存在左子树,则向右遍历

    1. struct TreeNode
    2. {
    3. int val;
    4. TreeNode* left = nullptr;
    5. TreeNode* right = nullptr;
    6. TreeNode(int v)
    7. :val(v)
    8. {}
    9. };
    1. void Morris(TreeNode* root)
    2. {
    3. while (root)
    4. {
    5. if (root->left)
    6. {
    7. TreeNode* right_left = root->left;
    8. while (right_left->right&& right_left != root)
    9. right_left = right_left->right;
    10. right_left->right = root;
    11. root = root->left;
    12. }
    13. else
    14. {
    15. cout << root->val << "->";
    16. root = root->right;
    17. }
    18. }
    19. cout << "nullptr";
    20. }
    1. int main()
    2. {
    3. TreeNode* n1 = new TreeNode(1);
    4. TreeNode* n3 = new TreeNode(3);
    5. n3->left = new TreeNode(2);
    6. n1->right = n3;
    7. TreeNode* n7 = new TreeNode(7);
    8. n7->right = new TreeNode(8);
    9. TreeNode* n6 = new TreeNode(6);
    10. n6->right = n7;
    11. n6->left = new TreeNode(5);
    12. TreeNode* n4 = new TreeNode(4);
    13. n4->left = n1;
    14. n4->right = n6;
    15. Morris(n4);
    16. return 0;
    17. }

    这样看貌似是没有问题的,但是我们要注意,在根节点向后遍历到左子树的最右节点时,下一个遍历的节点会重新根节点,这时,我们已经构建过该节点位置的线索二叉树了,因此这时我们需要之后直接遍历其右节点,因此我们需要对左子树存在的情况做一些处理

    1. void Morris(TreeNode* root)
    2. {
    3. while (root)
    4. {
    5. if (root->left)
    6. {
    7. TreeNode* right_left = root->left;
    8. while (right_left->right&& right_left != root)
    9. right_left = right_left->right;
    10. if (right_left != root)
    11. {
    12. right_left->right = root;
    13. root = root->left;
    14. }
    15. else
    16. {
    17. cout << root->val << "->";
    18. root = root->right;
    19. }
    20. }
    21. else
    22. {
    23. cout << root->val << "->";
    24. root = root->right;
    25. }
    26. }
    27. cout << "nullptr";
    28. }

    这样就不会出现问题了

     同时,为了保证二叉树保持原本的模样,我们可以在第二次遍历到根节点时删除构建的线索二叉树

    1. void Morris(TreeNode* root)
    2. {
    3. while (root)
    4. {
    5. if (root->left)
    6. {
    7. TreeNode* right_left = root->left;
    8. while (right_left->right&& right_left != root)
    9. right_left = right_left->right;
    10. if (right_left != root)
    11. {
    12. right_left->right = root;
    13. root = root->left;
    14. }
    15. else
    16. {
    17. right_left->right = nullptr;
    18. cout << root->val << "->";
    19. root = root->right;
    20. }
    21. }
    22. else
    23. {
    24. cout << root->val << "->";
    25. root = root->right;
    26. }
    27. }
    28. cout << "nullptr";
    29. }

     

    99. 恢复二叉搜索树 - 力扣(Leetcode)

    这道题就是运用的Morris遍历实现的空间复杂度为O(1)的算法

  • 相关阅读:
    R语言使用plot函数可视化数据散点图,移除坐标轴、通过设置axes参数为FALSE
    Java学习Day027(Java常见面试题21-25)
    操作系统期末知识点复习
    火山引擎 DataLeap:一家企业,数据体系要怎么搭建?
    jsp+ssm+maven大学生就业信息系统
    第二证券|12月A股投资方向来了!这些板块已先涨为敬
    8-13外部排序-败者树
    Web UI自动化测试框架
    关于 某讯QQ群的群文件上传和下载出现错误-134 的解决方法
    技术分享| Etcd如何实现分布式负载均衡及分布式通知与协调
  • 原文地址:https://blog.csdn.net/finish_speech/article/details/130843927