1. package com.atguigu.tree.threadedbinarytree;
    2. /**
    3. * ClassName: <br/>
    4. * Description: <br/>
    5. * Date: 2021-02-25 15:15 <br/>
    6. * @project data_algorithm
    7. * @package com.atguigu.tree
    8. */
    9. public class ThreadedBinaryTreeDemo {
    10. public static void main(String[] args) {
    11. //测试一把中序线索二叉树的功能
    12. HeroNode root = new HeroNode(1, "tom");
    13. HeroNode node2 = new HeroNode(3, "jack");
    14. HeroNode node3 = new HeroNode(6, "smith");
    15. HeroNode node4 = new HeroNode(8, "mary");
    16. HeroNode node5 = new HeroNode(10, "king");
    17. HeroNode node6 = new HeroNode(14, "dim");
    18. //二叉树,后面我们要递归创建, 现在简单处理使用手动创建
    19. root.setLeft(node2);
    20. root.setRight(node3);
    21. node2.setLeft(node4);
    22. node2.setRight(node5);
    23. node3.setLeft(node6);
    24. //测试中序线索化
    25. ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
    26. threadedBinaryTree.setRoot(root);
    27. threadedBinaryTree.threadedNodes();
    28. //测试: 以10号节点测试
    29. HeroNode leftNode = node5.getLeft();
    30. HeroNode rightNode = node5.getRight();
    31. System.out.println("10号结点的前驱结点是 =" + leftNode); //3
    32. System.out.println("10号结点的后继结点是=" + rightNode); //1
    33. //当线索化二叉树后,能在使用原来的遍历方法
    34. //threadedBinaryTree.infixOrder();
    35. System.out.println("使用线索化的方式遍历 线索化二叉树");
    36. threadedBinaryTree.threadedList(); // 8, 3, 10, 1, 14, 6
    37. }
    38. }
    39. //定义ThreadedBinaryTree 实现了线索化功能的二叉树
    40. class ThreadedBinaryTree {
    41. private HeroNode root;
    42. //为了实现线索化,需要创建要给指向当前结点的前驱结点的指针
    43. //在递归进行线索化时,pre 总是保留前一个结点
    44. private HeroNode pre = null;
    45. public void setRoot(HeroNode root) {
    46. this.root = root;
    47. }
    48. //重载一把threadedNodes方法
    49. public void threadedNodes() {
    50. this.threadedNodes(root);
    51. }
    52. //遍历线索化二叉树的方法
    53. public void threadedList() {
    54. //定义一个变量,存储当前遍历的结点,从root开始
    55. HeroNode node = root;
    56. while(node != null) {
    57. //循环的找到leftType == 1的结点,第一个找到就是8结点
    58. //后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
    59. //处理后的有效结点
    60. while(node.getLeftType() == 0) {
    61. node = node.getLeft();
    62. }
    63. //打印当前这个结点
    64. System.out.println(node);
    65. //如果当前结点的右指针指向的是后继结点,就一直输出
    66. while(node.getRightType() == 1) {
    67. //获取到当前结点的后继结点
    68. node = node.getRight();
    69. System.out.println(node);
    70. }
    71. //替换这个遍历的结点
    72. node = node.getRight();
    73. }
    74. }
    75. //编写对二叉树进行中序线索化的方法
    76. /**
    77. *
    78. * @param node 就是当前需要线索化的结点
    79. */
    80. public void threadedNodes(HeroNode node) {
    81. //如果node==null, 不能线索化
    82. if(node == null) {
    83. return;
    84. }
    85. //(一)先线索化左子树
    86. threadedNodes(node.getLeft());
    87. //(二)线索化当前结点[有难度]
    88. //处理当前结点的前驱结点
    89. //以8结点来理解
    90. //8结点的.left = null , 8结点的.leftType = 1
    91. if(node.getLeft() == null) {
    92. //让当前结点的左指针指向前驱结点
    93. node.setLeft(pre);
    94. //修改当前结点的左指针的类型,指向前驱结点
    95. node.setLeftType(1);
    96. }
    97. //处理后继结点
    98. if (pre != null && pre.getRight() == null) {
    99. //让前驱结点的右指针指向当前结点
    100. pre.setRight(node);
    101. //修改前驱结点的右指针类型
    102. pre.setRightType(1);
    103. }
    104. //!!! 每处理一个结点后,让当前结点是下一个结点的前驱结点
    105. pre = node;
    106. //(三)在线索化右子树
    107. threadedNodes(node.getRight());
    108. }
    109. //删除结点
    110. public void delNode(int no) {
    111. if(root != null) {
    112. //如果只有一个root结点, 这里立即判断root是不是就是要删除结点
    113. if(root.getNo() == no) {
    114. root = null;
    115. } else {
    116. //递归删除
    117. root.delNode(no);
    118. }
    119. }else{
    120. System.out.println("空树,不能删除~");
    121. }
    122. }
    123. //前序遍历
    124. public void preOrder() {
    125. if(this.root != null) {
    126. this.root.preOrder();
    127. }else {
    128. System.out.println("二叉树为空,无法遍历");
    129. }
    130. }
    131. //中序遍历
    132. public void infixOrder() {
    133. if(this.root != null) {
    134. this.root.infixOrder();
    135. }else {
    136. System.out.println("二叉树为空,无法遍历");
    137. }
    138. }
    139. //后序遍历
    140. public void postOrder() {
    141. if(this.root != null) {
    142. this.root.postOrder();
    143. }else {
    144. System.out.println("二叉树为空,无法遍历");
    145. }
    146. }
    147. //前序遍历
    148. public HeroNode preOrderSearch(int no) {
    149. if(root != null) {
    150. return root.preOrderSearch(no);
    151. } else {
    152. return null;
    153. }
    154. }
    155. //中序遍历
    156. public HeroNode infixOrderSearch(int no) {
    157. if(root != null) {
    158. return root.infixOrderSearch(no);
    159. }else {
    160. return null;
    161. }
    162. }
    163. //后序遍历
    164. public HeroNode postOrderSearch(int no) {
    165. if(root != null) {
    166. return this.root.postOrderSearch(no);
    167. }else {
    168. return null;
    169. }
    170. }
    171. }
    172. //先创建HeroNode 结点
    173. class HeroNode {
    174. private int no;
    175. private String name;
    176. private HeroNode left; //默认null
    177. private HeroNode right; //默认null
    178. //说明
    179. //1. 如果leftType == 0 表示指向的是左子树, 如果 1 则表示指向前驱结点
    180. //2. 如果rightType == 0 表示指向是右子树, 如果 1表示指向后继结点
    181. private int leftType;
    182. private int rightType;
    183. public int getLeftType() {
    184. return leftType;
    185. }
    186. public void setLeftType(int leftType) {
    187. this.leftType = leftType;
    188. }
    189. public int getRightType() {
    190. return rightType;
    191. }
    192. public void setRightType(int rightType) {
    193. this.rightType = rightType;
    194. }
    195. public HeroNode(int no, String name) {
    196. this.no = no;
    197. this.name = name;
    198. }
    199. public int getNo() {
    200. return no;
    201. }
    202. public void setNo(int no) {
    203. this.no = no;
    204. }
    205. public String getName() {
    206. return name;
    207. }
    208. public void setName(String name) {
    209. this.name = name;
    210. }
    211. public HeroNode getLeft() {
    212. return left;
    213. }
    214. public void setLeft(HeroNode left) {
    215. this.left = left;
    216. }
    217. public HeroNode getRight() {
    218. return right;
    219. }
    220. public void setRight(HeroNode right) {
    221. this.right = right;
    222. }
    223. @Override
    224. public String toString() {
    225. return "HeroNode [no=" + no + ", name=" + name + "]";
    226. }
    227. //递归删除结点
    228. //1.如果删除的节点是叶子节点,则删除该节点
    229. //2.如果删除的节点是非叶子节点,则删除该子树
    230. public void delNode(int no) {
    231. //思路
    232. /*
    233. * 1. 因为我们的二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除结点,而不能去判断当前这个结点是不是需要删除结点.
    234. 2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
    235. 3. 如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
    236. 4. 如果第2和第3步没有删除结点,那么我们就需要向左子树进行递归删除
    237. 5. 如果第4步也没有删除结点,则应当向右子树进行递归删除.
    238. */
    239. //2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
    240. if(this.left != null && this.left.no == no) {
    241. this.left = null;
    242. return;
    243. }
    244. //3.如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
    245. if(this.right != null && this.right.no == no) {
    246. this.right = null;
    247. return;
    248. }
    249. //4.我们就需要向左子树进行递归删除
    250. if(this.left != null) {
    251. this.left.delNode(no);
    252. }
    253. //5.则应当向右子树进行递归删除
    254. if(this.right != null) {
    255. this.right.delNode(no);
    256. }
    257. }
    258. //编写前序遍历的方法
    259. public void preOrder() {
    260. System.out.println(this); //先输出父结点
    261. //递归向左子树前序遍历
    262. if(this.left != null) {
    263. this.left.preOrder();
    264. }
    265. //递归向右子树前序遍历
    266. if(this.right != null) {
    267. this.right.preOrder();
    268. }
    269. }
    270. //中序遍历
    271. public void infixOrder() {
    272. //递归向左子树中序遍历
    273. if(this.left != null) {
    274. this.left.infixOrder();
    275. }
    276. //输出父结点
    277. System.out.println(this);
    278. //递归向右子树中序遍历
    279. if(this.right != null) {
    280. this.right.infixOrder();
    281. }
    282. }
    283. //后序遍历
    284. public void postOrder() {
    285. if(this.left != null) {
    286. this.left.postOrder();
    287. }
    288. if(this.right != null) {
    289. this.right.postOrder();
    290. }
    291. System.out.println(this);
    292. }
    293. //前序遍历查找
    294. /**
    295. *
    296. * @param no 查找no
    297. * @return 如果找到就返回该Node ,如果没有找到返回 null
    298. */
    299. public HeroNode preOrderSearch(int no) {
    300. System.out.println("进入前序遍历");
    301. //比较当前结点是不是
    302. if(this.no == no) {
    303. return this;
    304. }
    305. //1.则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找
    306. //2.如果左递归前序查找,找到结点,则返回
    307. HeroNode resNode = null;
    308. if(this.left != null) {
    309. resNode = this.left.preOrderSearch(no);
    310. }
    311. if(resNode != null) {//说明我们左子树找到
    312. return resNode;
    313. }
    314. //1.左递归前序查找,找到结点,则返回,否继续判断,
    315. //2.当前的结点的右子节点是否为空,如果不空,则继续向右递归前序查找
    316. if(this.right != null) {
    317. resNode = this.right.preOrderSearch(no);
    318. }
    319. return resNode;
    320. }
    321. //中序遍历查找
    322. public HeroNode infixOrderSearch(int no) {
    323. //判断当前结点的左子节点是否为空,如果不为空,则递归中序查找
    324. HeroNode resNode = null;
    325. if(this.left != null) {
    326. resNode = this.left.infixOrderSearch(no);
    327. }
    328. if(resNode != null) {
    329. return resNode;
    330. }
    331. System.out.println("进入中序查找");
    332. //如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前结点
    333. if(this.no == no) {
    334. return this;
    335. }
    336. //否则继续进行右递归的中序查找
    337. if(this.right != null) {
    338. resNode = this.right.infixOrderSearch(no);
    339. }
    340. return resNode;
    341. }
    342. //后序遍历查找
    343. public HeroNode postOrderSearch(int no) {
    344. //判断当前结点的左子节点是否为空,如果不为空,则递归后序查找
    345. HeroNode resNode = null;
    346. if(this.left != null) {
    347. resNode = this.left.postOrderSearch(no);
    348. }
    349. if(resNode != null) {//说明在左子树找到
    350. return resNode;
    351. }
    352. //如果左子树没有找到,则向右子树递归进行后序遍历查找
    353. if(this.right != null) {
    354. resNode = this.right.postOrderSearch(no);
    355. }
    356. if(resNode != null) {
    357. return resNode;
    358. }
    359. System.out.println("进入后序查找");
    360. //如果左右子树都没有找到,就比较当前结点是不是
    361. if(this.no == no) {
    362. return this;
    363. }
    364. return resNode;
    365. }
    366. }
    1. 10号结点的前驱结点是 =HeroNode [no=3, name=jack]
    2. 10号结点的后继结点是=HeroNode [no=1, name=tom]
    3. 使用线索化的方式遍历 线索化二叉树
    4. HeroNode [no=8, name=mary]
    5. HeroNode [no=3, name=jack]
    6. HeroNode [no=10, name=king]
    7. HeroNode [no=1, name=tom]
    8. HeroNode [no=14, name=dim]
    9. HeroNode [no=6, name=smith]
    10. Process finished with exit code 0