二叉树可以使用两种存储结构:顺序存储和二叉链表。在使用二叉链表的存储结构的过程中,会存在大量的空指针域,为了充分利用这些空指针域,引申出了“线索二叉树”,可以通过充分利用二叉链表中的空指针域,存放节点在某种遍历方式下的前驱和后继节点的指针。我们把这种指向前驱和后继的指针成为线索,加上线索的二叉链表成为线索链表,对应的二叉树就成为“线索二叉树”。

    线索化:
    现将某结点的空指针域指向该结点的前驱后继,定义规则如下:
    1、若结点的左子树为空,则该结点的左孩子指针指向其前驱结点。
    2、若结点的右子树为空,则该结点的右孩子指针指向其后继结点。
    这种指向前驱和后继的指针称为线索。将一棵普通二叉树以某种次序遍历,并添加线索的过程称为线索化。
    线索二叉树 - 图1

    1. /**
    2. * 《线索二叉树》
    3. */
    4. public class ThreadedBinaryTree {
    5. /**
    6. * 根结点
    7. */
    8. private TreeNode rootNode;
    9. /**
    10. * 线索化时记录前一个节点
    11. */
    12. private TreeNode preNode;
    13. /**
    14. * 结点类
    15. */
    16. private class TreeNode {
    17. private String name;
    18. private TreeNode left;
    19. private TreeNode right;
    20. // 左指针域类型 0:指向子结点、1:指向前驱结点
    21. private int leftType;
    22. // 右指针域类型 0:指向子结点、1:指向后继结点
    23. private int rightType;
    24. public TreeNode(String name, TreeNode left, TreeNode right) {
    25. this.name = name;
    26. this.left = left;
    27. this.right = right;
    28. }
    29. @Override
    30. public String toString() {
    31. return "TreeNode{" + "name='" + name + '\'' + '}';
    32. }
    33. }
    34. public ThreadedBinaryTree() {
    35. // 初始化二叉树
    36. // A
    37. // / \
    38. // B C
    39. // / \
    40. // D E
    41. TreeNode E = new TreeNode("E", null, null);
    42. TreeNode D = new TreeNode("D", null, null);
    43. TreeNode B = new TreeNode("B", D, E);
    44. TreeNode C = new TreeNode("C", null, null);
    45. this.rootNode = new TreeNode("A", B, C);
    46. }
    47. /**
    48. * 中序线索化二叉树
    49. */
    50. public void middleThreadOrder() {
    51. if (rootNode == null) {
    52. return;
    53. }
    54. middleThreadOrder(rootNode);
    55. }
    56. private void middleThreadOrder(TreeNode node) {
    57. if (node == null) {
    58. return;
    59. }
    60. // 1、处理左子树
    61. middleThreadOrder(node.left);
    62. // 2、线索化
    63. // 当前结点的左子结点为空时,指向前驱结点,且记录左指针域类型
    64. if (node.left == null) {
    65. node.left = preNode;
    66. node.leftType = 1;
    67. }
    68. // 前一结点的右子结点为空时,指向当前结点(后继结点),且记录右指针域类型
    69. if (preNode != null && preNode.right == null) {
    70. preNode.right = node;
    71. preNode.rightType = 1;
    72. }
    73. preNode = node;
    74. // 3、处理右子树
    75. middleThreadOrder(node.right);
    76. }
    77. /**
    78. * 中序遍历按后继方式线索化二叉树
    79. */
    80. public void middleOrderThreadListAfter() {
    81. TreeNode node = rootNode;
    82. while (node != null) {
    83. // 1、找中序遍历方式开始的节点
    84. while(node.left != null && node.leftType == 0) {
    85. node = node.left;
    86. }
    87. System.out.printf("%s ", node.name);
    88. // 2、如果右指针是线索,直接输出
    89. while (node.right != null && node.rightType == 1) {
    90. node = node.right;
    91. System.out.printf("%s ", node.name);
    92. }
    93. // 3、如果右指针不是线索,找到右子树开始的节点
    94. node = node.right;
    95. }
    96. }
    97. /**
    98. * 中序遍历按前驱方式线索化二叉树
    99. */
    100. public void middleOrderThreadListBefore() {
    101. TreeNode node = rootNode;
    102. while (node != null) {
    103. // 1、找中序遍历方式开始的节点
    104. while(node.right != null && node.rightType == 0) {
    105. node = node.right;
    106. }
    107. System.out.printf("%s ", node.name);
    108. // 2、如果左指针是线索,直接输出
    109. while (node.left != null && node.leftType == 1) {
    110. node = node.left;
    111. System.out.printf("%s ", node.name);
    112. }
    113. // 3、如果左指针不是线索,找到左子树开始的节点
    114. node = node.left;
    115. }
    116. }
    117. public static void main(String[] args) {
    118. ThreadedBinaryTree binaryTree = new ThreadedBinaryTree();
    119. // D B E A C
    120. binaryTree.middleThreadOrder();
    121. System.out.print("中序遍历按后继方式线索化二叉树:");
    122. binaryTree.middleOrderThreadListAfter();
    123. System.out.println();
    124. System.out.print("中序遍历按前驱方式线索化二叉树:");
    125. binaryTree.middleOrderThreadListBefore();
    126. }
    127. }