遍历线索化二叉树

说明:对前面的中序线索化的二叉树, 进行遍历
分析:因为线索化后,各个结点指向有变化,因此原来的遍历方式不能使用,这时需要使用新的方式遍历线索化二叉树,各个节点可以通过线型方式遍历,因此无需使用递归方式,这样也提高了遍历的效率。 遍历的次序应当和中序遍历保持一致。
代码:

线索二叉树应用案例

  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

判断左右的类型,然后沿着线索走一遍这个顺序

课后作业:

我这里讲解了中序线索化二叉树,前序线索化二叉树和后序线索化二叉树的分析思路类似,同学们作为课后作业完成.

这个线索要是弄好了,整明白了

遍历的过程不就是顺着线索走就成了么

交作业

前序 遍历 线索

  1. /**
  2. * 前序遍历 的中序的线索化二叉树的方法
  3. */
  4. public void threadedList() {
  5. //定义一个变量,存储当前遍历的结点,从root开始
  6. HeroNode node = root;
  7. // while(node != null) {
  8. // //循环的找到leftType == 1的结点,第一个找到就是8结点
  9. // //后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
  10. // //处理后的有效结点
  11. // while(node.getLeftType() == 0) {
  12. // node = node.getLeft();
  13. // }
  14. //
  15. // //打印当前这个结点
  16. // System.out.println(node);
  17. // //如果当前结点的右指针指向的是后继结点,就一直输出
  18. // while(node.getRightType() == 1) {
  19. // //获取到当前结点的后继结点
  20. // node = node.getRight();
  21. // System.out.println(node);
  22. // }
  23. // //替换这个遍历的结点
  24. // node = node.getRight();
  25. //
  26. // }
  27. // 中序是 先循环左,打印当前,在循环又
  28. // 这里写 前序就是 先打印,在循环左边,在循环右边
  29. while (node != null) {
  30. while(node.getLeftType() == 0) {
  31. System.out.println(node);
  32. node = node.getLeft();
  33. }
  34. System.out.println(node);
  35. while(node.getRightType() == 1) {
  36. //获取到当前结点的后继结点
  37. node = node.getRight();
  38. }
  39. node = node.getRight();
  40. }
  41. }

运行

  1. HeroNode [no=1, name=tom]
  2. HeroNode [no=3, name=jack]
  3. HeroNode [no=8, name=mary]
  4. HeroNode [no=10, name=king]
  5. HeroNode [no=6, name=smith]
  6. HeroNode [no=14, name=dim]
  7. Disconnected from the target VM, address: '127.0.0.1:50968', transport: 'socket'
  8. Process finished with exit code 0

? 大瑕疵,这里我在不知晴的情况下

在 利用 中序 线索化的 二叉树 中 实现了前序遍历

然后 在尝试实现后续遍历的时候,才发现的问题

????

所以要重新写一个后序对应的 线索化方法

才能用 后序的 遍历 线索化 二叉树方法

????

我服了

这tm也行

歪打正着

???

不用写 前序线索化方法,还能实现前序线索化的遍历 -> 二叉树

666666666666666666666666666666666666666666666666

哔哩哔哩动画
绝了’
img
img