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