1. /**
  2. * // 删除节点
  3. * @param no 编号
  4. */
  5. public void delNode(int no) {
  6. if (root != null) {
  7. // 如果只有一个root 节点
  8. // 这里立即判断root 是不是就要删除的结点
  9. if (root.getNo() == no) {
  10. root = null;
  11. } else {
  12. // 递归删除
  13. root.delNode(no);
  14. }
  15. } else {
  16. System.out.println("空数 ,不能删除~~~");
  17. }
  18. }
  1. /**
  2. * // 递归删除节点
  3. * // - 如果删除的节点是叶子节点,则删除该节点
  4. * //- 如果删除的节点是非叶子节点,则删除该子树.
  5. *
  6. * @param no
  7. */
  8. public void delNode(int no) {
  9. /*
  10. *
  11. 1. 因为我们的二叉树是单向的,所以我们判断当前节点的子节点是否需要删除节点,而不能去判断当前这个节点是不是需要删除节点.
  12. 3. 如果当前节点的右子树不为空,并且右子节点no就是要删除结点,就将this.right=null;并且就返回(结束递归删除)
  13. 4. 如果我们第二步和第三部都没有删除节点,那么我们就需要向左子树进行递归删除
  14. 5. 如果第四部也没有删除,则应当向右子树进行递归删除
  15. * */
  16. // 2. 如果当前节点的左子树不为空,并且左子节点no就是要删除结点,就将this.left=null; 并且就返回(结束递归删除)
  17. if (this.left != null && this.left.no == no) {
  18. this.left = null;
  19. return;
  20. }
  21. // 3. 如果当前节点的右子树不为空,并且右子节点no就是要删除结点,就将this.right=null;并且就返回(结束递归删除)
  22. if (this.right != null && this.right.no == no) {
  23. this.right = null;
  24. return;
  25. }
  26. // 4. 如果我们第二步和第三部都没有删除节点,那么我们就需要向左子树进行递归删除
  27. if (this.left != null) {
  28. this.left.delNode(no);
  29. }
  30. // 5. 如果第四部也没有删除,则应当向右子树进行递归删除
  31. if (this.right != null) {
  32. this.right.delNode(no);
  33. }
  34. }

测试

  1. // 测试一把删除节点
  2. System.out.println("\n删除钱,前序遍历");
  3. binaryTree.preOrder();
  4. // 执行删除
  5. binaryTree.delNode(5);
  6. System.out.println("\n删除后,前序遍历");
  7. binaryTree.preOrder();
  1. 删除钱,前序遍历
  2. HeroNode{no=1, name='松江'}
  3. HeroNode{no=2, name='吴用'}
  4. HeroNode{no=3, name='卢俊义'}
  5. HeroNode{no=5, name='关胜'}
  6. HeroNode{no=4, name='林冲'}
  7. 删除后,前序遍历
  8. HeroNode{no=1, name='松江'}
  9. HeroNode{no=2, name='吴用'}
  10. HeroNode{no=3, name='卢俊义'}
  11. HeroNode{no=4, name='林冲'}
  12. Process finished with exit code 0

完整代码

  1. package com.atguigu.tree;
  2. /**
  3. * @author victor
  4. * @site https://victorfengming.gitee.io/
  5. * @project data_algorithm
  6. * @package com.atguigu.tree
  7. * @created 2021-02-24 21:40
  8. */
  9. public class BinaryTreeDemo {
  10. public static void main(String[] args) {
  11. // 先需要创建一颗二叉树
  12. BinaryTree binaryTree = new BinaryTree();
  13. // 创建需要的节点
  14. HeroNode root = new HeroNode(1, "松江");
  15. HeroNode node2 = new HeroNode(2, "吴用");
  16. HeroNode node3 = new HeroNode(3, "卢俊义");
  17. HeroNode node4 = new HeroNode(4, "林冲");
  18. HeroNode node5 = new HeroNode(5, "关胜");
  19. // 说明: 这里我们先手动创建的二叉树
  20. // ,后面我们学习递归的方式创建二叉树
  21. root.setLeft(node2);
  22. root.setRight(node3);
  23. node3.setRight(node4);
  24. node3.setLeft(node5);
  25. binaryTree.setRoot(root);
  26. // 目前就挂载好了,二叉树的关系
  27. // 测试
  28. // System.out.println("前序遍历");//1,2,3,5,4
  29. // binaryTree.preOrder();
  30. // System.out.println("中序遍历");//2,1,5.3.4
  31. // binaryTree.infixOrder();
  32. // System.out.println("后序遍历");//2,5,4,3,1
  33. // binaryTree.postOrder();
  34. // 前序查找
  35. System.out.println("前序查找~~~");
  36. HeroNode resNode = binaryTree.preOrderSearch(2);
  37. if (resNode != null) {
  38. System.out.printf("找到了,信息为no=%d name=%s", resNode.getNo(), resNode.getName());
  39. } else {
  40. System.out.println("没有找到该英雄");
  41. }
  42. /*
  43. * 前序查找~~~
  44. * */
  45. // 中序遍历
  46. System.out.println("中序查找~~~");
  47. HeroNode resNode2 = binaryTree.infixOrderSearch(2);
  48. if (resNode2 != null) {
  49. System.out.printf("找到了,信息为no=%d name=%s", resNode2.getNo(), resNode2.getName());
  50. } else {
  51. System.out.println("没有找到该英雄");
  52. }
  53. /*
  54. 中序查找~~~
  55. * */
  56. System.out.println("后序查找~~~");
  57. HeroNode resNode3 = binaryTree.infixOrderSearch(2);
  58. if (resNode3 != null) {
  59. System.out.printf("找到了,信息为no=%d name=%s", resNode3.getNo(), resNode3.getName());
  60. } else {
  61. System.out.println("没有找到该英雄");
  62. }
  63. // 测试一把删除节点
  64. System.out.println("\n删除钱,前序遍历");
  65. binaryTree.preOrder();
  66. // 执行删除
  67. binaryTree.delNode(5);
  68. System.out.println("\n删除后,前序遍历");
  69. binaryTree.preOrder();
  70. }
  71. }
  72. // 定义一个 BinaryTree
  73. class BinaryTree {
  74. private HeroNode root;
  75. public void setRoot(HeroNode root) {
  76. this.root = root;
  77. }
  78. /**
  79. * // 删除节点
  80. *
  81. * @param no 编号
  82. */
  83. public void delNode(int no) {
  84. if (root != null) {
  85. // 如果只有一个root 节点
  86. // 这里立即判断root 是不是就要删除的结点
  87. if (root.getNo() == no) {
  88. root = null;
  89. } else {
  90. // 递归删除
  91. root.delNode(no);
  92. }
  93. } else {
  94. System.out.println("空数 ,不能删除~~~");
  95. }
  96. }
  97. // 真正的遍历操作
  98. // 前序遍历
  99. public void preOrder() {
  100. if (this.root != null) {
  101. this.root.preOrder();
  102. } else {
  103. System.out.println("当前二叉树为空,无法遍历!");
  104. }
  105. }
  106. // 中序遍历
  107. public void infixOrder() {
  108. if (this.root != null) {
  109. this.root.infixOrder();
  110. } else {
  111. System.out.println("二叉树为空,无法遍历");
  112. }
  113. }
  114. // 后续遍历
  115. public void postOrder() {
  116. if (this.root != null) {
  117. this.root.postOrder();
  118. } else {
  119. System.out.println("二叉树为空,无法遍历");
  120. }
  121. }
  122. /**
  123. * 前序查找
  124. *
  125. * @param no
  126. * @return
  127. */
  128. public HeroNode preOrderSearch(int no) {
  129. System.out.println("前序查找次数+1");
  130. if (root != null) {
  131. return root.preOrderSearch(no);
  132. } else {
  133. return null;
  134. }
  135. }
  136. /**
  137. * 中序查找
  138. *
  139. * @param no
  140. * @return
  141. */
  142. public HeroNode infixOrderSearch(int no) {
  143. if (root != null) {
  144. return root.infixOrderSearch(no);
  145. } else {
  146. return null;
  147. }
  148. }
  149. /**
  150. * 后序查找
  151. *
  152. * @param no
  153. * @return
  154. */
  155. public HeroNode postOrderSearch(int no) {
  156. if (root != null) {
  157. return root.postOrderSearch(no);
  158. } else {
  159. return null;
  160. }
  161. }
  162. }
  163. // 先创建 HeroNode 结点
  164. class HeroNode {
  165. private int no;
  166. private String name;
  167. private HeroNode left; // 默认为空
  168. private HeroNode right; // 默认为空
  169. public HeroNode(int no, String name) {
  170. this.no = no;
  171. this.name = name;
  172. }
  173. @Override
  174. public String toString() {
  175. return "HeroNode{" +
  176. "no=" + no +
  177. ", name='" + name + '\'' +
  178. '}';
  179. }
  180. public int getNo() {
  181. return no;
  182. }
  183. public void setNo(int no) {
  184. this.no = no;
  185. }
  186. public String getName() {
  187. return name;
  188. }
  189. public void setName(String name) {
  190. this.name = name;
  191. }
  192. public HeroNode getLeft() {
  193. return left;
  194. }
  195. public void setLeft(HeroNode left) {
  196. this.left = left;
  197. }
  198. public HeroNode getRight() {
  199. return right;
  200. }
  201. public void setRight(HeroNode right) {
  202. this.right = right;
  203. }
  204. /**
  205. * // 递归删除节点
  206. * // - 如果删除的节点是叶子节点,则删除该节点
  207. * //- 如果删除的节点是非叶子节点,则删除该子树.
  208. *
  209. * @param no
  210. */
  211. public void delNode(int no) {
  212. /*
  213. *
  214. 1. 因为我们的二叉树是单向的,所以我们判断当前节点的子节点是否需要删除节点,而不能去判断当前这个节点是不是需要删除节点.
  215. 3. 如果当前节点的右子树不为空,并且右子节点no就是要删除结点,就将this.right=null;并且就返回(结束递归删除)
  216. 4. 如果我们第二步和第三部都没有删除节点,那么我们就需要向左子树进行递归删除
  217. 5. 如果第四部也没有删除,则应当向右子树进行递归删除
  218. * */
  219. // 2. 如果当前节点的左子树不为空,并且左子节点no就是要删除结点,就将this.left=null; 并且就返回(结束递归删除)
  220. if (this.left != null && this.left.no == no) {
  221. this.left = null;
  222. return;
  223. }
  224. // 3. 如果当前节点的右子树不为空,并且右子节点no就是要删除结点,就将this.right=null;并且就返回(结束递归删除)
  225. if (this.right != null && this.right.no == no) {
  226. this.right = null;
  227. return;
  228. }
  229. // 4. 如果我们第二步和第三部都没有删除节点,那么我们就需要向左子树进行递归删除
  230. if (this.left != null) {
  231. this.left.delNode(no);
  232. }
  233. // 5. 如果第四部也没有删除,则应当向右子树进行递归删除
  234. if (this.right != null) {
  235. this.right.delNode(no);
  236. }
  237. }
  238. // 编写前序遍历的方法
  239. public void preOrder() {
  240. //
  241. System.out.println(this);
  242. // 先输出父节点
  243. // 递归向左子树前序比遍历
  244. if (this.left != null) {
  245. // 左边递归
  246. this.left.preOrder();
  247. }
  248. // 递归向右子树前序遍历
  249. if (this.right != null) {
  250. this.right.preOrder();
  251. }
  252. }
  253. // 中序遍历
  254. public void infixOrder() {
  255. // 递归向左子树中序遍历
  256. if (this.left != null) {
  257. this.left.infixOrder();
  258. }
  259. // 输出父节点
  260. System.out.println(this);
  261. // 递归向右子树遍历
  262. if (this.right != null) {
  263. this.right.infixOrder();
  264. }
  265. }
  266. // 后续遍历
  267. public void postOrder() {
  268. if (this.left != null) {
  269. this.left.postOrder();
  270. }
  271. if (this.right != null) {
  272. this.right.postOrder();
  273. }
  274. System.out.println(this);
  275. }
  276. /**
  277. * 前序查找
  278. *
  279. * @param no 查找no
  280. * @return 如果找到返回节点, 没有找到返回null
  281. */
  282. public HeroNode preOrderSearch(int no) {
  283. System.out.println("前序查找次数+1");
  284. // 比较当前节点是不是
  285. if (this.no == no) {
  286. return this;
  287. }
  288. // 1. 则判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
  289. // 2. 如果左递归前序查找,找到节点,则返回
  290. HeroNode resNode = null;
  291. if (this.left != null) {
  292. resNode = this.left.preOrderSearch(no);
  293. }
  294. if (resNode != null) {
  295. return resNode;
  296. }
  297. // 1. 左递归前序查找,找到节点,则返回,否则继续判断
  298. // 2. 当前的节点的右子节点是否为空,如果不空,则继续向右递归前序查找
  299. if (this.right != null) {
  300. resNode = this.right.preOrderSearch(no);
  301. }
  302. return resNode;
  303. }
  304. /**
  305. * 中序查找
  306. *
  307. * @param no
  308. * @return
  309. */
  310. public HeroNode infixOrderSearch(int no) {
  311. // 判断当前节点的左子节点是否为空
  312. HeroNode resNode = null;
  313. if (this.left != null) {
  314. resNode = this.left.infixOrderSearch(no);
  315. }
  316. if (resNode != null) {
  317. return resNode;
  318. }
  319. System.out.println("中序查找次数+1");
  320. // 如果找到则返回,如果没有找到,就和当前节点比较,如果是则返回当前节点
  321. if (this.no == no) {
  322. return this;
  323. }
  324. // 否则继续记性右递归的中序查找
  325. if (this.right != null) {
  326. resNode = this.right.infixOrderSearch(no);
  327. }
  328. return resNode;
  329. }
  330. /**
  331. * 后续查找
  332. *
  333. * @param no 序号
  334. * @return 查找到了返回 那啥 ,否则返回null
  335. */
  336. public HeroNode postOrderSearch(int no) {
  337. // 先判断当前节点的左子节点是否为空,如果不为空,
  338. // 则递归后需查找
  339. HeroNode resNode = null;
  340. if (this.left != null) {
  341. resNode = this.left.postOrderSearch(no);
  342. }
  343. if (resNode != null) {
  344. return resNode;
  345. }
  346. // 如果左子树没有找到,则向右子树递归进行后序变量查找
  347. if (this.right != null) {
  348. resNode = this.right.postOrderSearch(no);
  349. }
  350. if (resNode != null) {
  351. return resNode;
  352. }
  353. System.out.println("后序查找次数+1");
  354. // 如果 左右子树 都没有找到,就比较当前节点是不是
  355. if (this.no == no) {
  356. return this;
  357. }
  358. return resNode;
  359. }
  360. }

找到吴用后,看下面的左右子节点

但是很遗憾,吴用的左边是个遗憾

img
img

二叉树-删除节点

思考题(课后练习)

  1. 如果要删除的节点是非叶子节点,现在我们不希望将该非叶子节点为根节点的子树删除,需要指定规则, 假如规定如下:
  2. 如果该非叶子节点A只有一个子节点B,则子节点B替代节点A
  3. 如果该非叶子节点A有左子节点B和右子节点C,则让左子节点B替代节点A。
  4. 请大家思考,如何完成该删除功能, 老师给出提示.(课后练习)
  5. 后面在讲解 二叉排序树时,在给大家讲解具体的删除方法

img