• C语言实现线索化二叉树(先序、中序、后序)


    》》如何用C语言构建一颗二叉树? 

    第一种方法:

    1. ThreadTree A = (ThreadTree)malloc(sizeof(ThreadNode));
    2. A->data = { 'A' };
    3. A->ltag = 0;
    4. A->rtag = 0;
    5. A->lchild = NULL;
    6. A->rchild = NULL;
    7. ThreadTree B = (ThreadTree)malloc(sizeof(ThreadNode));
    8. B->data = { 'B' };
    9. A->lchild = B;
    10. B->ltag = 0;
    11. B->rtag = 0;
    12. B->lchild = NULL;
    13. B->rchild = NULL;
    14. ThreadTree C = (ThreadTree)malloc(sizeof(ThreadNode));
    15. C->data = { 'C' };
    16. A->rchild = C;
    17. C->lchild = NULL;
    18. C->rchild = NULL;
    19. C->ltag = 0;
    20. C->rtag = 0;
    21. ThreadTree D = (ThreadTree)malloc(sizeof(ThreadNode));
    22. D->data = { 'D' };
    23. B->lchild = D;
    24. D->lchild = NULL;
    25. D->rchild = NULL;
    26. D->ltag = 0;
    27. D->rtag = 0;
    28. ThreadTree E = (ThreadTree)malloc(sizeof(ThreadNode));
    29. E->data = { 'E' };
    30. B->rchild = E;
    31. E->lchild = NULL;
    32. E->rchild = NULL;
    33. E->ltag = 0;
    34. E->rtag = 0;
    35. ThreadTree F = (ThreadTree)malloc(sizeof(ThreadNode));
    36. F->data = { 'F' };
    37. C->lchild = F;
    38. F->lchild = NULL;
    39. F->rchild = NULL;
    40. F->ltag = 0;
    41. F->rtag = 0;
    42. ThreadTree G = (ThreadTree)malloc(sizeof(ThreadNode));
    43. G->data = { 'G' };
    44. D->rchild = G;
    45. G->lchild = NULL;
    46. G->rchild = NULL;
    47. G->ltag = 0;
    48. G->rtag = 0;

    第二种方法:

    》》了解为什么要利用线索的先前知识

    关键:n个结点的二叉树,有n+1个空链域!可用来记录前驱、后继的信息

    》》线索二叉树的存储结构

    》》中序线索二叉树

    • 前驱线索(由左孩子指针充当)
    • 后继线索(由右孩子指针充当)

    》》中序线索二叉树的存储

    》》中序线索化

     

     》》中序线索二叉树找中序后继

     》》中序线索二叉树找中序前驱

    》》完整代码:

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. typedef int ElemType;
    6. //线索二叉树结点
    7. typedef struct ThreadNode {
    8. ElemType data;
    9. struct ThreadNode* lchild, * rchild;
    10. int ltag, rtag;//左、右线索标志
    11. }ThreadNode, * ThreadTree;
    12. //创建二叉树
    13. ThreadNode* Create(ThreadNode* bt) {
    14. static int i = 0;
    15. char ch;
    16. //string str = "AB#D##C##";
    17. string str = "ABD#G##E##CF###";
    18. ch = str[i++];
    19. if (ch == '#')bt = NULL;//建立一棵空树
    20. else {
    21. bt = (ThreadTree)malloc(sizeof(ThreadNode)); bt->data = ch;//生成一个结点,数据域为ch
    22. bt->lchild = Create(bt->lchild);//递归建立左子树
    23. bt->rchild = Create(bt->rchild);//递归建立右子树
    24. bt->ltag = 0; bt->rtag = 0;
    25. }
    26. return bt;
    27. }
    28. //中序遍历
    29. void OrdinaryInOrder(ThreadNode* T) {
    30. if (T != NULL) {
    31. OrdinaryInOrder(T->lchild);//中序遍历左子树
    32. printf("%c ", T->data);//访问根节点的数据域
    33. OrdinaryInOrder(T->rchild);//中序遍历右子树
    34. }
    35. }
    36. //中序线索化
    37. void InThread(ThreadTree p,ThreadTree &pre) {
    38. if (p != NULL) {
    39. InThread(p->lchild,pre);//递归,线索化左子树
    40. if (p->lchild == NULL) {//左子树为空,建立前驱线索
    41. p->lchild = pre;
    42. p->ltag = 1;
    43. }
    44. if (pre != NULL && pre->rchild == NULL) {
    45. pre->rchild = p;//建立前驱结点的后继线索
    46. pre->rtag = 1;
    47. }
    48. pre = p;
    49. InThread(p->rchild,pre);//中序遍历右子树
    50. }
    51. }
    52. //中序线索化二叉树
    53. void CreateInThread(ThreadTree T) {
    54. ThreadTree pre = NULL;//pre 初始为NULL
    55. if (T != NULL) {//非空二叉树才能线索化
    56. InThread(T,pre);//中序线索化二叉树
    57. pre->rchild = NULL;//处理遍历的最后一个结点
    58. pre->rtag = 1;
    59. }
    60. }
    61. /*
    62. 在中序线索二叉树中找到指定结点 *p的中序后继next
    63. ①若p->rtag == 1,则next = p->rchild
    64. ②p->rtag == 0(p必定有右孩子)
    65. 中序遍历--左 根 右
    66. 左 根 (左 根 右)
    67. 左 根 ((左 根 右) 根 右)
    68. */
    69. //找到以P为根的子树中,第一个被中序遍历的结点
    70. ThreadNode* FirstNode(ThreadNode* p) {
    71. //循环找到最左下结点(不一定是叶结点)
    72. while (p->ltag == 0)
    73. p = p->lchild;
    74. return p;
    75. }
    76. //在中序线索二叉树中找到结点p的后继结点
    77. ThreadNode* NextNode(ThreadNode* p) {
    78. //右子树中最左下结点
    79. if (p->rtag == 0)
    80. return FirstNode(p->rchild);
    81. else
    82. return p->rchild;//rtag==1直接返回后继线索
    83. }
    84. //中序线索二叉树的中序遍历
    85. void InOrder(ThreadNode* T) {
    86. for (ThreadNode* p = FirstNode(T); p != NULL; p = NextNode(p)) {
    87. printf("%c ", p->data);//访问根节点的数据域
    88. }
    89. printf("\n");
    90. }
    91. /*
    92. 在中序线索二叉树中找到指定结点*p的中序前驱pre
    93. ①若p->ltag == 1,则pre = p->lchild;
    94. ②若p->ltag == 0(必有左孩子)
    95. 中序遍历--左 根 右
    96. (左 根 右) 根 右
    97. ((左 根 右) 根 右) 根 右
    98. */
    99. //找到以P为根的子树中,最后一个被中序遍历的结点
    100. ThreadNode* LastNode(ThreadNode* p) {
    101. //循环找到最右下结点(不一定是叶结点)
    102. while (p->rtag == 0)
    103. p = p->rchild;
    104. return p;
    105. }
    106. //在中序线索二叉树中找到结点p的前驱结点
    107. ThreadNode* PreNode(ThreadNode* p) {
    108. //左子树中最右下结点
    109. if (p->ltag == 0)
    110. return LastNode(p->lchild);
    111. else return p->lchild;//ltag == 1直接返回前驱线索
    112. }
    113. //对中序线索二叉树进行逆向中序遍历
    114. void RevInOrder(ThreadNode* T) {
    115. for (ThreadNode* p = LastNode(T); p != NULL; p = PreNode(p)) {
    116. printf("%c ", p->data);//访问根节点的数据域
    117. }
    118. printf("\n");
    119. }
    120. //寻找指定值的结点
    121. ThreadNode* findNode(ThreadTree t, int NodeData) {
    122. if (t == NULL) {
    123. return NULL;
    124. }
    125. if (t->data == NodeData) {
    126. return t;
    127. }
    128. ThreadNode* p;
    129. for (p = FirstNode(t); p != NULL; p = NextNode(p)) {
    130. if (p->data == NodeData) {
    131. return p;
    132. }
    133. }
    134. return NULL;
    135. }
    136. //思考:处理遍历的最后一个结点时,为什么没有判断rchild是否为NULL?
    137. //答案:中序遍历的最后一个结点右孩子指针必为空
    138. int main() {
    139. ThreadNode* p = (ThreadTree)malloc(sizeof(ThreadNode)); //记录目标节点
    140. p->data = 'C';//p指向目标结点
    141. //创建一棵二叉树
    142. ThreadTree T = (ThreadTree)malloc(sizeof(ThreadNode));//创建一颗二叉树
    143. T = Create(T);
    144. printf("---普通中序遍历--- \n");//B D A C
    145. OrdinaryInOrder(T);
    146. printf("\n");
    147. CreateInThread(T);
    148. printf("---中序线索二叉树找后继的中序遍历--- \n");//B D A C
    149. InOrder(T);
    150. printf("---中序线索二叉树找前驱的逆向的中序遍历--- \n");//B D A C
    151. RevInOrder(T);
    152. //printf("%c \n", findNode(T, p->data)->data);
    153. ThreadTree findCurrentNode = (ThreadTree)malloc(sizeof(ThreadNode));
    154. findCurrentNode = findNode(T, p->data);
    155. if (NextNode(findCurrentNode) != NULL) {
    156. printf("%c的后继是:%c", findCurrentNode->data, NextNode(findCurrentNode)->data);
    157. }
    158. else {
    159. printf("%c没有后继了", findCurrentNode->data);
    160. }
    161. printf("\n");
    162. if (PreNode(findCurrentNode) != NULL) {
    163. printf("%c的前驱是:%c", findCurrentNode->data, PreNode(findCurrentNode)->data);
    164. }
    165. else {
    166. printf("%c没有前驱了", findCurrentNode->data);
    167. }
    168. printf("\n");
    169. return 0;
    170. }

    》》实验效果

     

     

     

     

     

     》》先序线索二叉树

    》》先序线索二叉树的存储

    》》先序线索化(需要解决“转圈问题”)

     

    》》先序线索二叉树找先序后继

     》》先序线索二叉树找先序前驱

    》》总结

     》》先序线索二叉树找先序后继

    》》完整代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. typedef int ElemType;
    7. //线索二叉树结点
    8. typedef struct ThreadNode {
    9. ElemType data;
    10. struct ThreadNode* lchild, * rchild;
    11. int ltag, rtag;//左、右线索标志
    12. }ThreadNode, * ThreadTree;
    13. //创建二叉树
    14. ThreadNode* Create(ThreadNode* bt) {
    15. static int i = 0;
    16. char ch;
    17. //string str = "AB#D##C##";
    18. string str = "ABD#G##E##CF###";
    19. ch = str[i++];
    20. if (ch == '#')bt = NULL;//建立一棵空树
    21. else {
    22. bt = (ThreadTree)malloc(sizeof(ThreadNode)); bt->data = ch;//生成一个结点,数据域为ch
    23. bt->ltag = 0; bt->rtag = 0;
    24. bt->lchild = Create(bt->lchild);//递归建立左子树
    25. bt->rchild = Create(bt->rchild);//递归建立右子树
    26. }
    27. return bt;
    28. }
    29. //先序遍历
    30. void OrdinaryPreOrder(ThreadNode* T) {
    31. if (T != NULL) {
    32. printf("%c ", T->data);//访问根节点的数据域
    33. OrdinaryPreOrder(T->lchild);//先序遍历左子树
    34. OrdinaryPreOrder(T->rchild);//先序遍历右子树
    35. }
    36. }
    37. //先序线索化
    38. void PreThread(ThreadTree p, ThreadTree& pre) {
    39. if (p != NULL) {
    40. if (p->lchild == NULL) {//左子树为空,建立前驱线索
    41. p->lchild = pre;
    42. p->ltag = 1;
    43. }
    44. if (pre != NULL && pre->rchild == NULL) {
    45. pre->rchild = p;//建立前驱结点的后继线索
    46. pre->rtag = 1;
    47. }
    48. pre = p;
    49. //printf("%c", p->data);
    50. if (p->ltag == 0) {
    51. PreThread(p->lchild, pre);//递归,线索化左子树
    52. }
    53. if (p->rtag == 0) {
    54. PreThread(p->rchild, pre);
    55. }
    56. }
    57. }
    58. //先序线索化二叉树T
    59. void CreatePreThread(ThreadTree& T) {
    60. ThreadTree pre = NULL;//pre 初始为NULL
    61. if (T != NULL) {//非空二叉树才能线索化
    62. PreThread(T, pre);//先序线索化二叉树
    63. if(pre->rchild == NULL)
    64. pre->rtag = 1;
    65. }
    66. }
    67. /*
    68. 在先序线索二叉树中找到指定结点*p的先序后继(next)
    69. ①若p->rtag == 1,则 next = p->rchild
    70. ②若p->rtag == 0
    71. 》》若p有左孩子,则先序后继为左孩子
    72. 先序遍历---- 根 左 右
    73. 根 (根 左 右) 右
    74. 》》若p没有左孩子,则先序后继为右孩子
    75. 先序遍历---- 根 右
    76. 根 (根 左 右)
    77. 先序线索二叉树能否找到先序前驱呢?(提示:【先序遍历:根左右】)
    78. 答:不能,因为在左右子树中的结点只可能是根的后继,不可能是前驱。
    79. */
    80. //在先序线索二叉树中找到结点p的后继结点
    81. ThreadNode* NextNode(ThreadNode* p) {
    82. if (p->rtag == 1) {
    83. return p->rchild;
    84. }
    85. else {
    86. if (p->ltag == 0) //若p有左孩子,则先序后继为左孩子
    87. return p->lchild;
    88. else//若p没有左孩子,则先序后继为右孩子
    89. return p->rchild;
    90. }
    91. }
    92. //先序线索二叉树先序遍历
    93. void PreOrder(ThreadTree T) {
    94. for (ThreadNode* p = T; p != NULL; p = NextNode(p)) {
    95. printf("%c ", p->data);//访问根节点的数据域
    96. }
    97. printf("\n");
    98. }
    99. //寻找指定值的结点
    100. ThreadNode* findNode(ThreadTree T, int NodeData) {
    101. if (T == NULL) {
    102. return NULL;
    103. }
    104. if (T->data == NodeData) {
    105. return T;
    106. }
    107. ThreadNode* p;
    108. for (p = T; p != NULL; p = NextNode(p)) {
    109. if (p->data == NodeData) {
    110. return p;
    111. }
    112. }
    113. return NULL;
    114. }
    115. int main() {
    116. ThreadNode* p = (ThreadTree)malloc(sizeof(ThreadNode)); //记录目标节点
    117. p->data = 'C';//p指向目标结点
    118. //创建一棵二叉树
    119. ThreadTree T = (ThreadTree)malloc(sizeof(ThreadNode));//创建一颗二叉树
    120. T = Create(T);
    121. /*
    122. ThreadTree A = (ThreadTree)malloc(sizeof(ThreadNode));
    123. A->data = { 'A' };
    124. A->ltag = 0;
    125. A->rtag = 0;
    126. A->lchild = NULL;
    127. A->rchild = NULL;
    128. ThreadTree B = (ThreadTree)malloc(sizeof(ThreadNode));
    129. B->data = { 'B' };
    130. A->lchild = B;
    131. B->ltag = 0;
    132. B->rtag = 0;
    133. B->lchild = NULL;
    134. B->rchild = NULL;
    135. ThreadTree C = (ThreadTree)malloc(sizeof(ThreadNode));
    136. C->data = { 'C' };
    137. A->rchild = C;
    138. C->lchild = NULL;
    139. C->rchild = NULL;
    140. C->ltag = 0;
    141. C->rtag = 0;
    142. ThreadTree D = (ThreadTree)malloc(sizeof(ThreadNode));
    143. D->data = { 'D' };
    144. B->lchild = D;
    145. D->lchild = NULL;
    146. D->rchild = NULL;
    147. D->ltag = 0;
    148. D->rtag = 0;
    149. ThreadTree E = (ThreadTree)malloc(sizeof(ThreadNode));
    150. E->data = { 'E' };
    151. B->rchild = E;
    152. E->lchild = NULL;
    153. E->rchild = NULL;
    154. E->ltag = 0;
    155. E->rtag = 0;
    156. ThreadTree F = (ThreadTree)malloc(sizeof(ThreadNode));
    157. F->data = { 'F' };
    158. C->lchild = F;
    159. F->lchild = NULL;
    160. F->rchild = NULL;
    161. F->ltag = 0;
    162. F->rtag = 0;
    163. ThreadTree G = (ThreadTree)malloc(sizeof(ThreadNode));
    164. G->data = { 'G' };
    165. D->rchild = G;
    166. G->lchild = NULL;
    167. G->rchild = NULL;
    168. G->ltag = 0;
    169. G->rtag = 0;
    170. */
    171. printf("---普通先序遍历--- \n");
    172. OrdinaryPreOrder(T);
    173. printf("\n");
    174. //创建先序线索二叉树
    175. CreatePreThread(T);
    176. printf("---先序线索二叉树找后继的先序遍历--- \n");
    177. PreOrder(T);
    178. printf("\n");
    179. //寻找结点
    180. ThreadTree findCurrentNode = (ThreadTree)malloc(sizeof(ThreadNode));
    181. findCurrentNode = findNode(T, p->data);
    182. //寻找结点的后继结点
    183. if (NextNode(findCurrentNode) != NULL) {
    184. printf("%c 的后继是:%c", findCurrentNode->data, NextNode(findCurrentNode)->data);
    185. }
    186. else {
    187. printf("%c 没有后继了", findCurrentNode->data);
    188. }
    189. printf("\n");
    190. return 0;
    191. }

    》》实验结果

     

     》》后序线索二叉树

     》》后序线索二叉树的存储

    》》后序线索化

     

     

    王道的后序线索化,处理遍历的最后一个结点让pre->rchild = NULL,pre->rtag=1;

    但好像可以省去这一步。(仅个人看法,因为去掉之后运行也是正常的,如有错误,欢迎指正!!!) 

     》》后序线索二叉树找后序后继

     》》后序线索二叉树找后序前驱

    》》总结 

      》》后序线索二叉树找后序前驱

     》》完整代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. typedef int ElemType;
    7. //线索二叉树结点
    8. typedef struct ThreadNode {
    9. ElemType data;
    10. struct ThreadNode* lchild, * rchild;
    11. int ltag, rtag;//左、右线索标志
    12. }ThreadNode, * ThreadTree;
    13. ThreadNode* Create(ThreadNode* bt) {
    14. static int i = 0;
    15. char ch;
    16. //string str = "AB#D##C##";
    17. string str = "ABD#G##E##CF###";
    18. ch = str[i++];
    19. if (ch == '#')bt = NULL;//建立一棵空树
    20. else {
    21. bt = (ThreadTree)malloc(sizeof(ThreadNode)); bt->data = ch;//生成一个结点,数据域为ch
    22. bt->ltag = 0; bt->rtag = 0;
    23. bt->lchild = Create(bt->lchild);//递归建立左子树
    24. bt->rchild = Create(bt->rchild);//递归建立右子树
    25. }
    26. return bt;
    27. }
    28. //后序遍历
    29. void OrdinaryPostOrder(ThreadNode* T) {
    30. if (T != NULL) {
    31. OrdinaryPostOrder(T->lchild);//后序遍历左子树
    32. OrdinaryPostOrder(T->rchild);//后序遍历右子树
    33. printf("%c ", T->data);//访问根节点的数据域
    34. }
    35. }
    36. //后序线索化
    37. void PostThread(ThreadTree p, ThreadTree& pre) {
    38. if (p != NULL) {
    39. PostThread(p->lchild, pre);//递归,线索化左子树
    40. PostThread(p->rchild, pre);//递归,线索化右子树
    41. if (p->lchild == NULL) {//左子树为空,建立前驱线索
    42. p->lchild = pre;
    43. p->ltag = 1;
    44. }
    45. if (pre != NULL && pre->rchild == NULL) {
    46. pre->rchild = p;//建立前驱结点的后继线索
    47. pre->rtag = 1;
    48. }
    49. pre = p;//标记当前结点成为刚刚访问的结点
    50. }
    51. }
    52. //后序线索化二叉树
    53. void CreatePostThread(ThreadTree T) {
    54. ThreadTree pre = NULL;
    55. if (T != NULL) {//非空二叉树才能线索化
    56. PostThread(T, pre);//线索化二叉树
    57. if(pre->rchild == NULL)//处理遍历的最后一个结点
    58. pre->rtag = 1;
    59. }
    60. }
    61. /*
    62. 在后序线索二叉树中找到指定结点*p的后序前驱(pre)
    63. ①若p->ltag == 1,则 pre = p->lchild
    64. ②若p->ltag == 0
    65. 》》若p有右孩子,则后序前驱为右孩子
    66. 先序遍历---- 左 右 根
    67. 左 (左 右 根) 根
    68. 》》若p没有右孩子,则后序前驱为左孩子
    69. 先序遍历---- 左 根
    70. (左 右 根) 根
    71. 后序线索二叉树能否找到后序后继呢?(提示:【后序遍历:左右根】)
    72. 答:不能,因为在左右子树中的结点只可能是根的前驱,不可能是后继。
    73. */
    74. //在后序线索二叉树中找到结点p的前驱结点
    75. ThreadNode* PreNode(ThreadNode* p) {
    76. if (p->ltag == 1)
    77. return p->lchild;
    78. else {
    79. if (p->rtag == 0) {
    80. return p->rchild;
    81. }
    82. else {
    83. return p->lchild;
    84. }
    85. }
    86. }
    87. void RevPostOrder(ThreadTree T) {
    88. for (ThreadNode* p = T; p != NULL; p = PreNode(p)) {
    89. printf("%c ", p->data);//访问根节点的数据域
    90. }
    91. printf("\n");
    92. }
    93. //寻找指定值的结点
    94. ThreadNode* findNode(ThreadTree T, int NodeData) {
    95. if (T == NULL) {
    96. return NULL;
    97. }
    98. if (T->data == NodeData) {
    99. return T;
    100. }
    101. ThreadNode* p;
    102. for (p = T; p != NULL; p = PreNode(p)) {
    103. if (p->data == NodeData) {
    104. return p;
    105. }
    106. }
    107. return NULL;
    108. }
    109. int main() {
    110. ThreadNode* p = (ThreadTree)malloc(sizeof(ThreadNode)); //记录目标节点
    111. p->data = 'A';//p指向目标结点
    112. //创建一棵二叉树
    113. ThreadTree T = (ThreadTree)malloc(sizeof(ThreadNode));//创建一颗二叉树
    114. T = Create(T);
    115. printf("---普通后序遍历--- \n");
    116. OrdinaryPostOrder(T);
    117. printf("\n");
    118. //创建后序线索二叉树
    119. CreatePostThread(T);
    120. printf("---后序线索二叉树找前驱的逆向的后序遍历--- \n");
    121. RevPostOrder(T);
    122. printf("\n");
    123. //寻找结点
    124. ThreadNode* findCurrentNode = (ThreadTree)malloc(sizeof(ThreadNode));
    125. findCurrentNode = findNode(T, p->data);
    126. if (PreNode(findCurrentNode) != NULL) {
    127. printf("%c的前驱是:%c", findCurrentNode->data, PreNode(findCurrentNode)->data);
    128. }
    129. else {
    130. printf("%c没有前驱了", findCurrentNode->data);
    131. }
    132. printf("\n");
    133. return 0;
    134. }

    》》实验结果

     

     

    》》三种线索二叉树的对比

    本文是根据王道考研的数据结构教学视频中,运用其分析原理思想,再结合自己的一些思路分析和逻辑编程,进行完整的代码的编写。如有不正确的地方,欢迎指正!

  • 相关阅读:
    C++信息学奥赛1184:明明的随机数
    关于#网络协议#的问题:哪位知道京东跳转qq支付代付 会的联系我哪位知道京东跳转qq支付代付 会的联系我
    电脑重装系统c盘如何备份资料
    aws的eks平滑删除work节点实现降配
    7 种提升 Spring Boot 吞吐量神技
    注册大量短视频矩阵账号很简单,这个方法教会你,还有这个批量剪辑神器帮你完成矩阵分发
    利用数据分析告警机制,实现鸿鹄与飞书双向集成
    MongoDB内部的存储原理
    STM32微控制器的低功耗模式
    Unity 轮播图
  • 原文地址:https://blog.csdn.net/weixin_41987016/article/details/127877030