深圳幻海软件技术有限公司 欢迎您!

线索二叉树(图解+完整代码)

2023-05-06

目录⚽1.问题🏐2.线索化🏀 3.线索化带来的问题与解决🥎4.完整代码⚽1.问题我们的二叉树学到现在,会产生两个问题:在n个结点的二叉树中,必定有n+1个空链域(叶子结点的左右子树空间浪费了)二叉树的遍历,无论是递归还是非递归算法,效率都不算高。那我们能不能利用原本浪费掉的空间,来解

目录

⚽1.问题

🏐2.线索化

🏀 3.线索化带来的问题与解决

🥎4.完整代码


1.问题

我们的二叉树学到现在,会产生两个问题:

  • 在n个结点的二叉树中,必定有n+1个空链域(叶子结点的左右子树空间浪费了)
  • 二叉树的遍历,无论是递归还是非递归算法,效率都不算高。

那我们能不能利用原本浪费掉的空间,来解决第二个问题呢?

倘若对下图二叉树进行中序遍历,可以得到1、3、6、8、10,我们可以知道3的前驱结点为1,后驱结点为6。但是,这种关系的建立是在完成遍历后得到的,那么,可不可以在建立二叉树的同时记录下前驱后继的关系,这样我们在寻找前驱后继结点时的效率将会大大提升!

我们的前辈们给出了答案:线索化 

🏐2.线索化

现将某结点的空指针域指向该结点的前驱后继,定义规则如下:

  • 若结点的左子树为空,则该结点的左孩子指针指向其前驱结点
  • 若结点的右子树为空,则该结点的右孩子指针指向其后继结点

这种指向前驱和后继的指针称为线索,将一棵普通的二叉树以某种次序遍历,并添加线索的过程称为线索化。

🏀 3.线索化带来的问题与解决

此时新的问题又产生了:我们如何区分一个lchild指针是指向左孩子还是前驱结点呢?

为了解决这个问题,我们需要添加标志位ltag和rtag,并定义以下规则

  • ltag==0,指向左孩子;ltag==1,指向前驱结点
  • rtag==0,指向右孩子;rtag==1,指向后继结点

🥎4.完整代码

中序线索化

  1. void inOrderThreadTree(Node* node)
  2. {
  3. //如果当前结点为NULL 直接返回
  4. if (node == NULL) {
  5. return;
  6. }
  7. //先处理左子树
  8. inOrderThreadTree(node->left_node);
  9. if (node->left_node == NULL)
  10. {
  11. //设置前驱结点
  12. node->left_type = 1;
  13. node->left_node = pre;
  14. }
  15. //如果结点的右子节点为NULL 处理前驱的右指针
  16. if (pre !=NULL && pre->right_node == NULL)
  17. {
  18. //设置后继
  19. pre->right_node = node;
  20. pre->right_type = 1;
  21. }
  22. //每处理一个节点 当前结点是下一个节点的前驱
  23. pre = node;
  24. //最后处理右子树
  25. inOrderThreadTree(node->right_node);
  26. }

遍历

  1. void inOrderTraverse(Node* root)
  2. {
  3. //从根节点开始先找到最左边
  4. if (root == NULL)
  5. {
  6. return;
  7. }
  8. Node* temp = root;
  9. //先找到最左边结点 然后根据线索化直接向右遍历
  10. while (temp != NULL && temp->left_type == 0)
  11. {
  12. temp = temp->left_node;
  13. }
  14. while (temp != NULL)
  15. {
  16. //输出
  17. temp = temp->right_node;
  18. }
  19. }

完整代码

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. typedef struct Thread {
  4. struct Thread* left_node, *right_node;//左右指针
  5. int data;//需要存放的数据
  6. /*默认0代表左右孩子 1代表前驱或者后继*/
  7. int left_type;//类型标志
  8. int right_type;//类型标志
  9. }Node;
  10. Node* pre;//前驱结点的变量
  11. Node* head;//头指针 指向某种遍历的第一个结点
  12. void inOrderThreadTree(Node* node)
  13. {
  14. //如果当前结点为NULL 直接返回
  15. if (node == NULL) {
  16. return;
  17. }
  18. //先处理左子树
  19. inOrderThreadTree(node->left_node);
  20. if (node->left_node == NULL)
  21. {
  22. //设置前驱结点
  23. node->left_type = 1;
  24. node->left_node = pre;
  25. }
  26. //如果结点的右子节点为NULL 处理前驱的右指针
  27. if (pre !=NULL && pre->right_node == NULL)
  28. {
  29. //设置后继
  30. pre->right_node = node;
  31. pre->right_type = 1;
  32. }
  33. //每处理一个节点 当前结点是下一个节点的前驱
  34. pre = node;
  35. //最后处理右子树
  36. inOrderThreadTree(node->right_node);
  37. }
  38. void inOrderTraverse(Node* root)
  39. {
  40. //从根节点开始先找到最左边
  41. if (root == NULL)
  42. {
  43. return;
  44. }
  45. Node* temp = root;
  46. //先找到最左边结点 然后根据线索化直接向右遍历
  47. while (temp != NULL && temp->left_type == 0)
  48. {
  49. temp = temp->left_node;
  50. }
  51. while (temp != NULL)
  52. {
  53. //输出
  54. temp = temp->right_node;
  55. }
  56. }

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览45641 人正在系统学习中