当前位置:   article > 正文

平衡二叉树java实现(插入、删除、平衡调整)_java 二叉平衡树

java 二叉平衡树

目录

一、排序二叉树与平衡二叉树区别

二、失衡类型及失衡调整

1、RR型

2、LL型

3、RL型

4、LR型

三、插入节点

1、插入操作

2、失衡节点

3、失衡类型

四、删除节点

1、删除操作

2、失衡节点

3、失衡类型 

五、代码实现

1、平衡二叉树java类

2、测试类

3、测试打印

六、相关证明

1、插入操作,只需调整最小失衡子树,调整后整颗树会恢复平衡

2、删除节点无子节点,失衡节点只有一个


一、排序二叉树与平衡二叉树区别

排序二叉树:是一颗二叉树,且所有节点的右孩子比自身大,自身比左孩子大

平衡二叉树:在排序二叉树的定义之下,再加一个条件,即所有节点左子树和右子树高度之差的绝对值不大于1

二、失衡类型及失衡调整

1、RR型

1)失衡节点作为旋转节点

2)失衡节点的子节点作为中心节点

3)旋转节点绕中心节点左旋,如果中心节点存在左子树,那么该左子树变成旋转节点的右子树

4)再把中心节点挂到原来的父节点上

5)总结:左旋

2、LL型

同RR型相反,右旋,略过

3、RL

1)先以失衡节点的子节点作为旋转节点,失衡节点的孙子节点作为中心节点,右旋

2)如果中心节点存在右子树,则该右子树变成旋转节点的左子树

3)将中心节点连接到失衡节点的右孩子处

4)再已失衡节点作为旋转节点,以失衡节点的孙子节点作为中心节点,左旋

5)如果中心节点存在左子树,则该左子树变成旋转节点的右子树

6)将中心节点挂到原来的父节点上

7)总结:先右旋,后左旋

4、LR型

同RL型相反,先左旋,后右旋,略过

三、插入节点

1、插入操作

从根节点向下遍历,找到插入节点的父节点,然后插入

2、失衡节点

1)插入新节点可能会导致多个节点失衡

2)插入造成的失衡,失衡节点只可能是插入节点的父节点或更上层的祖先节点(爷爷、祖父...)

3)插入操作,只需调整最小失衡子树,调整后整颗树会恢复平衡,证明见6.1

3、失衡类型

1)从插入节点向上遍历,直到找到第一个失衡节点(最小失衡子树根节点)

2)从失衡节点向插入节点遍历,前两个路径即失衡类型

3)举例

四、删除节点

1、删除操作

1)待删除节点无子节点——直接删除(节点替换,用null替换删除节点)

2)待删除节点仅有左子树或右子树——直接用左子树或右子树替换删除的节点(节点替换,用直接子节点替换删除节点)

3)待删除节点既有左子树,又有右子树——找右子树中最小的节点minNode,用minNode的值替换删除节点的值(值替换),再用minNode的右子树替换minNode(节点替换)

  • 找到待删除节点右子树中最小的节点minNode,即26节点
  • 用26节点的值替换待删除节点的值(值替换)
  • 删除掉minNode(节点替换)
  • 注:minNode也可以找被删除节点左子树最大节点
2、失衡节点

1)删除节点无子节点

  • 失衡节点只可能是被删除被删除节点的上层节点(父亲、爷爷、祖父...),子树1、子树2仍然保持平衡

  • 失衡节点只可能有一个,证明见6.2

2)删除节点有且仅有左子树(右子树)

  • 失衡节点只可能是被删除被删除节点的上层节点(父亲、爷爷、祖父...),子树1、子树2、子树3仍然保持平衡

  • 失衡节点只可能有一个,证明同6.2

3)删除节点既有左子树又有右子树

  • 失衡节点只可能是minNode(被删除节点右子树最小节点的父节点)的祖先结点(父亲、爷爷、祖父...)

  • 失衡节点只可能有一个,证明同6.2

3、失衡类型 

从失衡节点向左右子树中,较高的子树遍历,得到前两个路径即为失衡类型,举例

五、代码实现

话不多说,show you my code

1、平衡二叉树java类
  1. package com.yqc.tree;
  2. import lombok.Data;
  3. import java.util.Objects;
  4. /**
  5. * @author Rickon
  6. * @date 2023/11/16 18:21
  7. * @description
  8. * 1、一个根节点属性root
  9. * 2、主要方法:
  10. * 插入、删除、调整、左旋、右旋、节点替换、查找指定值节点、判断节点是否失衡、判断右子树是否高于左子树
  11. * 前序遍历、中序遍历、后续遍历、层序遍历
  12. * 树的高度、叶子节点数、所有节点数、第i层节点数
  13. */
  14. @Data
  15. public class BSTree {
  16. // 操作类型
  17. private static final int OPERATION_INSERT = 1;
  18. private static final int OPERATION_DELETE = 2;
  19. // 根节点属性
  20. private Node root;
  21. /**
  22. * 插入
  23. */
  24. public void insert(Integer val) {
  25. Node newNode = new Node(val, null);
  26. // 插入第一个节点
  27. if (null == this.root) {
  28. this.root = newNode;
  29. return;
  30. }
  31. // 找到待插入节点的父节点
  32. Node temp = root;
  33. Node parent = null;
  34. while (null != temp) {
  35. parent = temp;
  36. if (val > temp.val)
  37. temp = temp.rChild;
  38. else if (val < temp.val)
  39. temp = temp.lChild;
  40. else
  41. // 待插入节点已存在
  42. return;
  43. }
  44. // 插入
  45. newNode.parent = parent;
  46. if (val > parent.val)
  47. parent.rChild = newNode;
  48. else
  49. parent.lChild = newNode;
  50. // 调整至平衡
  51. adjust(newNode, OPERATION_INSERT);
  52. }
  53. /**
  54. * 删除
  55. */
  56. public void remove(Integer val) {
  57. // 找到待删除节点
  58. Node removedNode = get(val);
  59. if (null == removedNode)
  60. return;
  61. // 保存节点,用来寻找失衡节点的起始节点
  62. Node beginNode = null;
  63. // 分情况处理
  64. if (null == removedNode.lChild && null == removedNode.rChild) {
  65. // 失衡节点只能是删除节点上层节点(父亲,爷爷,祖父...)
  66. beginNode = removedNode.parent;
  67. // 直接删除
  68. replace(removedNode, null);
  69. } else if (null != removedNode.lChild && null != removedNode.rChild) {
  70. // 找右子树中最小的节点minNode(也可找左子树中最大的节点)
  71. Node minNode = null;
  72. Node temp = removedNode.rChild;
  73. while (null != temp) {
  74. minNode = temp;
  75. temp = temp.lChild;
  76. }
  77. // 失衡节点只能是minNode上层节点(父亲,爷爷,祖父...)
  78. beginNode = minNode.parent;
  79. // minNode的右子树替换minNode(右子树提升,相对位置变化,右子树最多只有一个节点,否则minNode就失衡了)
  80. replace(minNode, minNode.rChild);
  81. // 待删除节点val替换成minNode的val(节点值互换,相对位置不变)
  82. removedNode.val = minNode.val;
  83. } else {
  84. // 失衡节点只能是删除节点上层节点(父亲,爷爷,祖父...)
  85. beginNode = removedNode.parent;
  86. // 保存待删除节点左子树或右子树
  87. Node child;
  88. if (null != removedNode.lChild)
  89. child = removedNode.lChild;
  90. else
  91. child = removedNode.rChild;
  92. // 左子树或右子树替换待删除节点
  93. replace(removedNode, child);
  94. }
  95. // 调整
  96. adjust(beginNode, OPERATION_DELETE);
  97. }
  98. /**
  99. * 用target替换source的位置(位置替换,注意区别值替换)
  100. */
  101. private void replace(Node source, Node target) {
  102. if (source == null)
  103. throw new RuntimeException("被替换节点不能为空");
  104. Node parent = source.parent;
  105. // 用null替换
  106. if (target == null) {
  107. if (source.val > parent.val)
  108. parent.rChild = null;
  109. else
  110. parent.lChild = null;
  111. source.parent = null;
  112. source.lChild = null;
  113. source.rChild = null;
  114. return;
  115. }
  116. if (Objects.equals(source.val, target.val))
  117. throw new RuntimeException("被替换节点与目标几点值相同");
  118. // 用非空节点替换
  119. if (source.val > parent.val)
  120. parent.rChild = target;
  121. else
  122. parent.lChild = target;
  123. target.parent = parent;
  124. source.parent = null;
  125. source.lChild = null;
  126. source.rChild = null;
  127. }
  128. /**
  129. * 查询指定节点
  130. */
  131. private Node get(Integer val) {
  132. Node temp = root;
  133. Node removedNode = null;
  134. while (null != temp) {
  135. if (val > temp.val)
  136. temp = temp.rChild;
  137. else if (val < temp.val)
  138. temp = temp.lChild;
  139. else {
  140. removedNode = temp;
  141. break;
  142. }
  143. }
  144. return removedNode;
  145. }
  146. /**
  147. * 插入或删除节点后,重新调整为平衡二叉树
  148. * @param node 插入操作——插入节点,删除操作——找失衡节点的起始节点
  149. * @param operationType 操作类型
  150. */
  151. private void adjust(Node node, int operationType) {
  152. // 从插入或起始节点向上遍历,找失衡节点(最小失衡子树根节点)
  153. Node temp = node;
  154. Node unbalancedNode = null;
  155. while (temp != null) {
  156. if (isUnbalanced(temp)) {
  157. unbalancedNode = temp;
  158. break;
  159. } else {
  160. temp = temp.parent;
  161. }
  162. }
  163. if (null == unbalancedNode)
  164. return;
  165. // 判断失衡类型
  166. StringBuilder sb = new StringBuilder();
  167. temp = unbalancedNode;
  168. Node unbalancedChildNode = null;
  169. Node unbalancedGrandChildNode = null;
  170. if (OPERATION_INSERT == operationType) {
  171. // 从失衡节点向插入节点搜索两次,判断失衡类型
  172. for (int i=0; i<2; ++i) {
  173. if (node.val > temp.val) {
  174. sb.append("R");
  175. temp = temp.rChild;
  176. } else {
  177. sb.append("L");
  178. temp = temp.lChild;
  179. }
  180. // 记录【失衡节点儿子节点】和【失衡节点孙子节点】
  181. if (unbalancedChildNode == null)
  182. unbalancedChildNode = temp;
  183. else
  184. unbalancedGrandChildNode = temp;
  185. }
  186. } else {
  187. // 从失衡节点向更高子树遍历搜索两次,判断失衡类型
  188. for (int i=0; i<2; ++i) {
  189. if (isRChildHigher(temp)) {
  190. sb.append("R");
  191. temp = temp.rChild;
  192. } else {
  193. sb.append("L");
  194. temp = temp.lChild;
  195. }
  196. // 记录【失衡节点儿子节点】和【失衡节点孙子节点】
  197. if (unbalancedChildNode == null)
  198. unbalancedChildNode = temp;
  199. else
  200. unbalancedGrandChildNode = temp;
  201. }
  202. }
  203. String unbalancedType = sb.toString();
  204. // 根据失衡类型调整
  205. // 记录失衡节点的父节点,最小失衡子树调整后需要挂在该父节点上,如果为空,说明根节点就是最小失衡子树的根节点
  206. Node parent = unbalancedNode.parent;
  207. boolean isRChild = false;
  208. if (null != parent) {
  209. isRChild = unbalancedNode.val > parent.val;
  210. }
  211. switch (unbalancedType) {
  212. case "RR":
  213. // 左旋
  214. lRotate(unbalancedNode, unbalancedChildNode, parent, isRChild);
  215. break;
  216. case "RL":
  217. // 先右旋,后左旋
  218. rRotate(unbalancedChildNode, unbalancedGrandChildNode, unbalancedNode, true);
  219. lRotate(unbalancedNode, unbalancedGrandChildNode, parent, isRChild);
  220. break;
  221. case "LL":
  222. // 右旋
  223. rRotate(unbalancedNode, unbalancedChildNode, parent, isRChild);
  224. break;
  225. case "LR":
  226. // 先左旋,后右旋
  227. lRotate(unbalancedChildNode, unbalancedGrandChildNode, unbalancedNode, false);
  228. rRotate(unbalancedNode, unbalancedGrandChildNode, parent, isRChild);
  229. break;
  230. default:
  231. throw new RuntimeException("失衡类型异常");
  232. }
  233. }
  234. /**
  235. * 判断节点右子树高度是否高于左子树
  236. */
  237. private boolean isRChildHigher(Node root) {
  238. if (root == null) {
  239. return false;
  240. }
  241. return height(root.getRChild()) > height(root.getLChild());
  242. }
  243. /**
  244. * 左旋
  245. * @param rotatedNode 旋转节点
  246. * @param centerNode 中心节点
  247. * @param parent 旋转后,中心节点的父节点
  248. * @param isRChild 旋转后,中心节点是否作为parent的右孩子
  249. */
  250. private void lRotate(Node rotatedNode, Node centerNode, Node parent, boolean isRChild) {
  251. // 保存【中心节点】的左孩子
  252. Node centerNodeLChild = centerNode.lChild;
  253. // 【旋转节点】变成【中心节点】的左孩子
  254. centerNode.lChild = rotatedNode;
  255. rotatedNode.parent = centerNode;
  256. rotatedNode.rChild = null;
  257. // 【中心节点】的左孩子变成【旋转节点】的右孩子
  258. if (centerNodeLChild != null) {
  259. rotatedNode.rChild = centerNodeLChild;
  260. centerNodeLChild.parent = rotatedNode;
  261. }
  262. // 旋转后重新连接到parent,parent为null,表示调整了整棵树
  263. if (null == parent)
  264. root = centerNode;
  265. else if (isRChild)
  266. parent.rChild = centerNode;
  267. else
  268. parent.lChild = centerNode;
  269. centerNode.parent = parent;
  270. }
  271. /**
  272. * 右旋
  273. * @param rotatedNode 旋转节点
  274. * @param centerNode 中心节点
  275. * @param parent 旋转后,中心节点的父节点
  276. * @param isRChild 旋转后,中心节点是否作为parent的右孩子
  277. */
  278. private void rRotate(Node rotatedNode, Node centerNode, Node parent, boolean isRChild) {
  279. // 保存【中心节点】的左孩子
  280. Node centerNodeRChild = centerNode.rChild;
  281. // 【旋转节点】变成【中心节点】的右孩子
  282. centerNode.rChild = rotatedNode;
  283. rotatedNode.parent = centerNode;
  284. rotatedNode.lChild = null;
  285. // 【中心节点】的左孩子变成【旋转节点】的右孩子
  286. if (centerNodeRChild != null) {
  287. rotatedNode.lChild = centerNodeRChild;
  288. centerNodeRChild.parent = rotatedNode;
  289. }
  290. // 旋转后重新连接到parent,parent为null,表示调整了整棵树
  291. if (null == parent)
  292. root = centerNode;
  293. else if (isRChild)
  294. parent.rChild = centerNode;
  295. else
  296. parent.lChild = centerNode;
  297. centerNode.parent = parent;
  298. }
  299. /**
  300. * 判断节点是否是失衡
  301. */
  302. private boolean isUnbalanced(Node root) {
  303. if (null == root) {
  304. return false;
  305. }
  306. return Math.abs(height(root.lChild) - height(root.rChild)) > 1;
  307. }
  308. /**
  309. * 前序遍历
  310. */
  311. public void preOrder() {
  312. preOrder(this.root);
  313. System.out.println();
  314. }
  315. private void preOrder(Node root) {
  316. if (null == root) {
  317. return;
  318. }
  319. System.out.print(root + " ");
  320. preOrder(root.lChild);
  321. preOrder(root.rChild);
  322. }
  323. /**
  324. * 中序遍历
  325. */
  326. public void midOrder() {
  327. midOrder(this.root);
  328. System.out.println();
  329. }
  330. private void midOrder(Node root) {
  331. if (null == root) {
  332. return;
  333. }
  334. midOrder(root.lChild);
  335. System.out.print(root + " ");
  336. midOrder(root.rChild);
  337. }
  338. /**
  339. * 后序遍历
  340. */
  341. public void postOrder() {
  342. postOrder(this.root);
  343. System.out.println();
  344. }
  345. private void postOrder(Node root) {
  346. if (null == root) {
  347. return;
  348. }
  349. postOrder(root.lChild);
  350. postOrder(root.rChild);
  351. System.out.print(root + " ");
  352. }
  353. /**
  354. * 层序遍历
  355. */
  356. public void levelOrder() {
  357. if (null == root) {
  358. return;
  359. }
  360. // 根节点入队
  361. Queue queue = new Queue(10);
  362. queue.push(root);
  363. // 队列不空
  364. while (!queue.isEmpty()) {
  365. // 出队并打印
  366. Node node = queue.pop();
  367. System.out.print(node + " ");
  368. // 左子树入队
  369. if (null != node.lChild) {
  370. queue.push(node.lChild);
  371. }
  372. // 右子树入队
  373. if (null != node.rChild) {
  374. queue.push(node.rChild);
  375. }
  376. }
  377. System.out.println();
  378. }
  379. /**
  380. * 第i层的节点数
  381. */
  382. public int countOfLevel(int i) {
  383. return countOfLevel(root, i);
  384. }
  385. private int countOfLevel(Node root, int i) {
  386. if (null == root) {
  387. return 0;
  388. }
  389. if (1 == i) {
  390. return 1;
  391. }
  392. return countOfLevel(root.lChild, i - 1) + countOfLevel(root.rChild, i - 1);
  393. }
  394. /**
  395. * 树的高度
  396. */
  397. public int height() {
  398. return height(root);
  399. }
  400. private int height(Node root) {
  401. if (null == root) {
  402. return 0;
  403. }
  404. return Math.max(1 + height(root.lChild), 1 + height(root.rChild));
  405. }
  406. /**
  407. * 叶子节点个数
  408. */
  409. public int leafCount() {
  410. return leafCount(root);
  411. }
  412. private static int leafCount(Node root) {
  413. if (null == root) {
  414. return 0;
  415. }
  416. if (null == root.lChild && null == root.rChild) {
  417. return 1;
  418. }
  419. return leafCount(root.lChild) + leafCount(root.rChild);
  420. }
  421. /**
  422. * 所有节点个数
  423. */
  424. public int size() {
  425. return size(root);
  426. }
  427. private static int size(Node root) {
  428. if (null == root) {
  429. return 0;
  430. }
  431. return 1 + size(root.lChild) + size(root.rChild);
  432. }
  433. @Data
  434. static class Node{
  435. Integer val;
  436. Node parent;
  437. Node lChild;
  438. Node rChild;
  439. public Node(Integer val, Node parent) {
  440. this.val = val;
  441. this.parent = parent;
  442. }
  443. // 一定要重写toString(),因为父子节点相互持有对方的引用,不重写会导致循环打印,stackOverFlow
  444. @Override
  445. public String toString() {
  446. return val + "";
  447. }
  448. }
  449. @Data
  450. static class Queue {
  451. Node[] queue;
  452. int size;
  453. int head;
  454. int tail;
  455. int capacity;
  456. public Queue(int capacity) {
  457. this.queue = new Node[capacity];
  458. this.size = 0;
  459. this.head = 0;
  460. this.tail = 0;
  461. this.capacity = capacity;
  462. }
  463. /**
  464. * 判断队列是否已满
  465. */
  466. public boolean isFull() {
  467. return this.getSize() >= this.getCapacity();
  468. }
  469. /**
  470. * 判断队列是否为空
  471. */
  472. public boolean isEmpty() {
  473. return this.getSize() <= 0;
  474. }
  475. /**
  476. * 入队
  477. */
  478. public boolean push(Node element) {
  479. if (isFull()) {
  480. throw new RuntimeException("队列已满");
  481. }
  482. queue[tail] = element;
  483. tail = (tail + 1) % capacity;
  484. size++;
  485. return true;
  486. }
  487. /**
  488. * 出队
  489. */
  490. public Node pop() {
  491. if (isEmpty()) {
  492. throw new RuntimeException("队列为空");
  493. }
  494. Node element = queue[head];
  495. head = (head + 1) % capacity;
  496. size--;
  497. return element;
  498. }
  499. }
  500. }
2、测试类
  1. package com.yqc.tree;
  2. /**
  3. * @author Rickon
  4. * @date 2023/11/14 20:34
  5. * @description
  6. */
  7. public class Test {
  8. public static void main(String[] args) {
  9. BSTree tree = new BSTree();
  10. tree.insert(10);
  11. tree.insert(20);
  12. tree.insert(5);
  13. tree.insert(37);
  14. tree.insert(39);
  15. tree.insert(15);
  16. Test.print(tree);
  17. tree.remove(10);
  18. tree.remove(39);
  19. tree.remove(37);
  20. Test.print(tree);
  21. System.out.println("节点总数:" + tree.size());
  22. System.out.println("叶子节点数:" + tree.leafCount());
  23. System.out.println("第2层节点数:" + tree.countOfLevel(2));
  24. System.out.println("树的高度:" + tree.height());
  25. }
  26. private static void print(BSTree tree) {
  27. System.out.print("先序:");tree.preOrder();
  28. System.out.print("中序:");tree.midOrder();
  29. System.out.print("后序:");tree.postOrder();
  30. System.out.print("层序:");tree.levelOrder();
  31. }
  32. }
3、测试打印

六、相关证明

1、插入操作,只需调整最小失衡子树,调整后整颗树会恢复平衡

1)在节点插入前,节点20的左子树高度为H+1;插入后,节点20的左子树高度为H+2,导致节点20衡

2)在节点插入前,节点10的右子树高度为H;插入后,节点10的右子树高度由H增高为H+1,导致节点10失衡

3)节点插入后,10和20两个节点失衡,最小失衡子树为虚线绿框所示,根节点即失衡节点10

4)以10为根节点的子树,是20节点的左子树,在新节点插入前,其高度为H+1,插入后,高度为H+2(变为最小失衡子树),在调整平衡后(调整最小失衡子树),高度又变为H+1,所以节点20从平衡变为失衡,又因为最小失衡子树的调整,20节点重新达到平衡

2、删除节点无子节点,失衡节点只有一个

1)删除节点18,如果要使得节点10失衡,那么在删除操作前,节点10的左子树【子树1】的高度必须比右子树高1

2)删除节点18后,节点10的右子树高度减1,导致节点10失衡

3)但是从节点20看,它的左子树高度不变不会因为节点18的删除而改变,仍然保持平衡

4)同理,更高层祖先节点也不会失衡

3、删除既有左子树又有右子树的节点,失衡节点只可能是minNode的父节点

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/528378
推荐阅读
相关标签
  

闽ICP备14008679号