当前位置:   article > 正文

平衡二叉树(AVL树)算法 Java实现_计算二叉树bf值java

计算二叉树bf值java

定义:

    平衡二叉树(Self-Balancing Binary Search Tree 或 Height-Balanced Binary Search Tree),是一种二叉排序树,其中每一个结点的左子树和右子树的高度差至多等于1。

    将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF(balance Factor)。那么平衡二叉树上所有结点的平衡因子只可能是-1,0和1.

    距离插入结点最近的,且平衡因子的绝对值大于1的结点为跟的子树,我们称之为最小不平衡子树


算法分析:

    递归法插入结点后,如果检测到某个结点的左子树或右子树“长高”了而导致这棵树不平衡,那么就需要对其进行左平衡或右平衡,此结点就是最小不平衡子树的根节点

    在左平衡或右平衡时,当最小不平衡子树根节点的平衡因子是大于1时,就右旋,小于-1时就左旋;最小不平衡子树的BF与它对应子树(右旋时要对应左子树,左旋时对应右子树)的BF 符号(正负)相反时,就需要对子树先进行一次反向旋转以使得符号相同后,再旋转不平衡子树根节点才能够完成平衡操作。


示例图片:

P:最小不平衡子树的根节点

L:P左子树的根节点

LR:L右子树的根节点

N:新插入的结点

插入时的左平衡右平衡

  左平衡(即右旋最小不平衡子树)

    一.BF符号相同(PL都为正):

      情况一:

          操作:无论N是LL的左子树还是右子树,直接右旋P

          BF结果:P:0;L:0

      情况二:

          操作:右旋P

          BF结果:P:0;L:0

.BF符号不同(PL一正一负)

          情况一:N在LR的左子树,LR 的BF 值为 1

              操作:先左旋L,再右旋P

              BF结果:P:-1; L:0; LR:0

情况二:N是LR,LR的BF值为0

            操作:先左旋L,再右旋P

            BF结果:P:0; L:0; LR(N):0

情况三:N在LR的右子树,LR的 BF 值为 -1

            操作:先左旋L,再右旋P

            BF结果:P:0; L:1; LR:0

右平衡(即左旋最小不平衡子树)

          和左平衡道理一样,BF符号相同不相同的情况图在最下。


删除时的左平衡右平衡

 删除的左平衡和右平衡包含插入时的所有情况,结合图可以轻易理解,但是删除有特殊情况,插入时不可能会出现的。

特殊情况为:

左平衡 L 结点 BF为0

操作:右旋P

BF结果:P:1  L:-1

注意:这种删除的特殊情况左平衡后,树并没有变矮,而其他情况全部会变矮

右平衡也相同道理,见图


代码:

  1. import java.util.Random;
  2. import java.util.concurrent.ConcurrentLinkedQueue;
  3. public class AVLTree {
  4. /** 根结点 */
  5. private TreeNode rootNode;
  6. /** 插入时判断是否变高 */
  7. private boolean taller;
  8. /** 删除时判断是否变矮*/
  9. private boolean lower;
  10. /**
  11. * 插入
  12. * @param key
  13. * @return
  14. */
  15. public boolean insertAVL(int key) {
  16. if (this.rootNode == null) {
  17. rootNode = new TreeNode(key, 0);
  18. return true;
  19. }
  20. return insertAVL(key, this.rootNode, null, null);
  21. }
  22. public boolean insertAVL(int key,
  23. TreeNode node, TreeNode parentNode, Boolean leftOrRight) {
  24. //插入新结点,树“长高”,至taller为true
  25. if (node == null) {
  26. node = new TreeNode(key, 0);
  27. if (leftOrRight) {
  28. parentNode.setLchild(node);
  29. } else {
  30. parentNode.setRchild(node);
  31. }
  32. //第一层必须判断长高没有,所以必须为true
  33. taller = true;
  34. // 插入了新结点返回true ,没有返回false
  35. return true;
  36. }
  37. // 树中已存在和e有相同关键字的结点则不再插入
  38. if (key == node.getData()) {
  39. taller = false;
  40. return false;
  41. }
  42. // 小于则再左子树中继续搜索
  43. if (key < node.getData()) {
  44. // 插入了新结点返回true ,没有返回false
  45. if (!insertAVL(key, node.getLchild(), node, true))
  46. return false;
  47. if (taller) {
  48. switch (node.getBf()) {
  49. case 1:
  50. leftBalance(node);
  51. taller = false;
  52. break;
  53. case 0:
  54. node.setBf(1);
  55. taller = true;
  56. break;
  57. case -1:
  58. node.setBf(0);
  59. taller = false;
  60. break;
  61. }
  62. }
  63. // 大于则在右子树中继续查找
  64. } else {
  65. if (!insertAVL(key, node.getRchild(), node, false))
  66. return false;
  67. if (taller) {
  68. switch (node.getBf()) {
  69. case 1:
  70. node.setBf(0);
  71. taller = false;
  72. break;
  73. case 0:
  74. node.setBf(-1);
  75. taller = true;
  76. break;
  77. case -1:
  78. rightBalance(node);
  79. taller = false;
  80. break;
  81. }
  82. }
  83. }
  84. return true;
  85. }
  86. /**
  87. * 删除结点
  88. * @param key
  89. * @return
  90. */
  91. public boolean removeAVL(int key) {
  92. if (this.rootNode == null) {
  93. return false;
  94. }
  95. return removeAVL(key, this.rootNode, null, null);
  96. }
  97. public boolean removeAVL(int key,
  98. TreeNode node, TreeNode parentNode, Boolean leftOrRight) {
  99. //没有找到要删除的结点
  100. if (node == null) {
  101. return false;
  102. }
  103. //找到要删除的结点
  104. if (key == node.getData()) {
  105. //要删除的结点 左右子树都不为空
  106. if (node.getLchild() != null && node.getRchild() != null) {
  107. // 删除前驱结点,将前驱结点的值放入当前结点处
  108. TreeNode precursorNode = precursorNode(node);
  109. removeAVL(precursorNode.getData(), node.getLchild(), node, true);
  110. // 替换
  111. // 必须在旋转之前,不然就会替换错目标
  112. node.setData(precursorNode.getData());
  113. // 前驱必定是在左子树中
  114. if (lower) {
  115. switch (node.getBf()) {
  116. case 1:
  117. node.setBf(0);
  118. lower = true;
  119. break;
  120. case 0:
  121. node.setBf(-1);
  122. lower = false;
  123. break;
  124. case -1:
  125. // 当出现删除时独有的情况(rchild bf 0)经过平衡后 并没有变矮
  126. if (node.getRchild().getBf() == 0)
  127. lower = false;
  128. else
  129. lower = true;
  130. rightBalance(node);
  131. break;
  132. }
  133. }
  134. return true;
  135. // 左子树为空、右子树为空或者两者都为空
  136. } else {
  137. // 根结点(特殊情况)
  138. if (parentNode == null) {
  139. rootNode = node.getLchild() == null ? node.getRchild() : node.getLchild();
  140. return true;
  141. }
  142. if (leftOrRight) {
  143. parentNode.setLchild(
  144. node.getLchild() == null ? node.getRchild() : node.getLchild());
  145. } else {
  146. parentNode.setRchild(
  147. node.getLchild() == null ? node.getRchild() : node.getLchild());
  148. }
  149. //删除结点后 树变低了
  150. lower = true;
  151. return true;
  152. }
  153. }
  154. // 继续在左子树或右子树中查找
  155. if (key < node.getData()) {
  156. if (!removeAVL(key, node.getLchild(), node, true))
  157. return false;
  158. // 注意:删除后变低的情况和插入变高的情况完全相反,
  159. // 如删除后进行平衡旋转实际上整体变低一层。结合图理解
  160. if (lower) {
  161. switch (node.getBf()) {
  162. case 1:
  163. node.setBf(0);
  164. lower = true;
  165. break;
  166. case 0:
  167. node.setBf(-1);
  168. lower = false;
  169. break;
  170. case -1:
  171. // 当出现删除时独有的情况(rchild bf 0)经过平衡后 并没有变矮
  172. if (node.getRchild().getBf() == 0)
  173. lower = false;
  174. else
  175. lower = true;
  176. rightBalance(node);
  177. break;
  178. }
  179. }
  180. } else {
  181. if (!removeAVL(key, node.getRchild(), node, false))
  182. return false;
  183. if (lower) {
  184. switch (node.getBf()) {
  185. case 1:
  186. // 当出现删除时独有的情况(lchild bf 0)经过平衡后 并没有变矮
  187. if (node.getLchild().getBf() == 0)
  188. lower = false;
  189. else
  190. lower = true;
  191. leftBalance(node);
  192. break;
  193. case 0:
  194. node.setBf(1);
  195. lower = false;
  196. break;
  197. case -1:
  198. node.setBf(0);
  199. lower = true;
  200. break;
  201. }
  202. }
  203. }
  204. return true;
  205. }
  206. /**
  207. * 找前驱
  208. * @param node
  209. * @return
  210. */
  211. private TreeNode precursorNode(TreeNode node) {
  212. if (node == null)
  213. return null;
  214. TreeNode precursorNode = node.getLchild();
  215. while (precursorNode != null) {
  216. if (precursorNode.getRchild() != null)
  217. precursorNode = precursorNode.getRchild();
  218. else
  219. break;
  220. }
  221. return precursorNode;
  222. }
  223. /**
  224. * 对以node为根的二叉排序树做右旋处理 <p>
  225. * 处理之后node 为新的树根结点,即旋转处理之前的左子树的根结点
  226. */
  227. public void rightRotate(TreeNode node) {
  228. TreeNode leftNode = node.getLchild();
  229. TreeNode rightNode = node.getRchild();
  230. TreeNode rootNode = new TreeNode(node.getData(), node.getBf());
  231. rootNode.setLchild(leftNode);
  232. rootNode.setRchild(rightNode);
  233. node.setData(leftNode.getData());
  234. node.setBf(leftNode.getBf());
  235. node.setLchild(leftNode.getLchild());
  236. node.setRchild(rootNode);
  237. rootNode.setLchild(leftNode.getRchild());
  238. }
  239. /**
  240. * 对以node 为根的二叉排序树做左旋处理<p>
  241. * 处理之后node 为新的树根结点,即旋转处理之前的右子树的根结点
  242. */
  243. public void leftRotate(TreeNode node) {
  244. TreeNode leftNode = node.getLchild();
  245. TreeNode rightNode = node.getRchild();
  246. TreeNode rootNode = new TreeNode(node.getData(), node.getBf());
  247. rootNode.setLchild(leftNode);
  248. rootNode.setRchild(rightNode);
  249. node.setData(rightNode.getData());
  250. node.setBf(rightNode.getBf());
  251. node.setLchild(rootNode);
  252. node.setRchild(rightNode.getRchild());
  253. rootNode.setRchild(rightNode.getLchild());
  254. }
  255. /**
  256. * 对以 node结点为根的二叉树作左平衡旋转处理<p>
  257. * 以node为根结点的树就是最小不平衡二叉树,且左子树的高度大于右子树,平衡因子大于1
  258. */
  259. public void leftBalance(TreeNode node) {
  260. TreeNode leftNode = node.getLchild();
  261. switch (leftNode.getBf()) {
  262. // 左结点和node结点平衡因子符号相同,直接右旋最小不平衡树
  263. case 1:
  264. leftNode.setBf(0);
  265. node.setBf(0);
  266. rightRotate(node);
  267. break;
  268. // 左结点和node结点平衡因子符号不同,先左旋左孩子,再右旋最小不平衡树
  269. case -1:
  270. TreeNode leftNodeRchild = leftNode.getRchild();
  271. switch (leftNodeRchild.getBf()) {
  272. case 1:
  273. node.setBf(-1);
  274. leftNode.setBf(0);
  275. break;
  276. case 0:
  277. node.setBf(0);
  278. leftNode.setBf(0);
  279. break;
  280. case -1:
  281. node.setBf(0);
  282. leftNode.setBf(1);
  283. break;
  284. }
  285. leftNodeRchild.setBf(0);
  286. leftRotate(leftNode);
  287. rightRotate(node);
  288. break;
  289. //主要用于删除。插入时不可能会有这种情况
  290. //并且平衡后树的高度并没有变矮,而其他两种情况树会变矮
  291. case 0:
  292. node.setBf(1);
  293. leftNode.setBf(-1);
  294. rightRotate(node);
  295. break;
  296. }
  297. }
  298. public void rightBalance(TreeNode node) {
  299. TreeNode rightNode = node.getRchild();
  300. switch (rightNode.getBf()) {
  301. case -1:
  302. node.setBf(0);
  303. rightNode.setBf(0);
  304. leftRotate(node);
  305. break;
  306. case 1:
  307. TreeNode rightNodeLchild = rightNode.getLchild();
  308. switch (rightNodeLchild.getBf()) {
  309. case 1:
  310. node.setBf(0);
  311. rightNode.setBf(-1);
  312. break;
  313. case 0:
  314. node.setBf(0);
  315. rightNode.setBf(0);
  316. break;
  317. case -1:
  318. node.setBf(1);
  319. rightNode.setBf(0);
  320. break;
  321. }
  322. rightNodeLchild.setBf(0);
  323. rightRotate(rightNode);
  324. leftRotate(node);
  325. break;
  326. //主要用于删除。插入时不可能会有这种情况
  327. //并且平衡后树的高度并没有变矮,而其他两种情况树会变矮
  328. case 0:
  329. node.setBf(-1);
  330. rightNode.setBf(1);
  331. leftRotate(node);
  332. break;
  333. }
  334. }
  335. /**
  336. * 打印树
  337. */
  338. public void printTree() {
  339. ConcurrentLinkedQueue<TreeNode> queue = new ConcurrentLinkedQueue<>();
  340. ConcurrentLinkedQueue<TreeNode> tempQueue = new ConcurrentLinkedQueue<>();
  341. queue.add(this.rootNode);
  342. int offset = 0;
  343. int counter = 2;
  344. for (int i = 0; i < 50; i++)
  345. System.out.print(" ");
  346. while (queue.peek() != null) {
  347. TreeNode node = queue.poll();
  348. // 找 node的parentNode begin
  349. TreeNode parentNode = null;
  350. TreeNode searchNode = this.rootNode;
  351. int key = node.getData();
  352. while (true) {
  353. if (key < searchNode.getData()) {
  354. parentNode = searchNode;
  355. searchNode = searchNode.getLchild();
  356. } else if (key > searchNode.getData()){
  357. parentNode = searchNode;
  358. searchNode = searchNode.getRchild();
  359. } else {
  360. break;
  361. }
  362. }
  363. // --- end
  364. String side = "L";
  365. if (parentNode != null && parentNode.getRchild() == node)
  366. side = "R";
  367. System.out.print(node.getData() + "(" + (parentNode == null ? "" : parentNode.getData()) + " " + side + ")");
  368. if (parentNode != null && parentNode.getRchild() != node)
  369. for (int i = 0; i < counter; i++)
  370. System.out.print(" ");
  371. if (node.getLchild() != null)
  372. tempQueue.add(node.getLchild());
  373. if (node.getRchild() != null)
  374. tempQueue.add(node.getRchild());
  375. if (queue.isEmpty()) {
  376. offset += 3;
  377. // counter--;
  378. copyQueue(tempQueue, queue);
  379. System.out.println();
  380. for (int i = 0; i < 50 - offset; i++)
  381. System.out.print(" ");
  382. }
  383. }
  384. }
  385. private void copyQueue(ConcurrentLinkedQueue<TreeNode> source, ConcurrentLinkedQueue<TreeNode> target) {
  386. while (source.peek() != null) {
  387. target.add(source.poll());
  388. }
  389. }
  390. public TreeNode getRootNode() {
  391. return this.rootNode;
  392. }
  393. //中序遍历
  394. public void inOrderTraverse(TreeNode node){
  395. if(node != null){
  396. // 左,根,右
  397. inOrderTraverse(node.getLchild());
  398. System.out.print(node.getData() + " ");
  399. inOrderTraverse(node.getRchild());
  400. }
  401. }
  402. public static void main(String[] args) {
  403. int[] arr = {3, 2, 1, 4, 5, 6, 7, 10, 9, 8};
  404. AVLTree tree = new AVLTree();
  405. for (int a : arr) {
  406. tree.insertAVL(a);
  407. }
  408. tree.printTree();
  409. System.out.println("\n----------------remove begin-----------------");
  410. // 随机删除
  411. for (int i = 0; i < 10; i++) {
  412. int index = new Random().nextInt(10) + 1;
  413. System.out.println("\n删除:" + index);
  414. tree.removeAVL(index);
  415. tree.inOrderTraverse(tree.getRootNode());
  416. System.out.println();
  417. tree.printTree();
  418. }
  419. }
  420. }
  421. /**
  422. * 二叉树结点
  423. */
  424. class TreeNode {
  425. private int data;
  426. /** 结点的平衡因子 新增 */
  427. private int bf;
  428. private TreeNode lchild;
  429. private TreeNode rchild;
  430. public TreeNode(int data, int bf) {
  431. super();
  432. this.data = data;
  433. this.bf = bf;
  434. }
  435. public int getData() {
  436. return data;
  437. }
  438. public void setData(int data) {
  439. this.data = data;
  440. }
  441. public int getBf() {
  442. return bf;
  443. }
  444. public void setBf(int bf) {
  445. this.bf = bf;
  446. }
  447. public TreeNode getLchild() {
  448. return lchild;
  449. }
  450. public void setLchild(TreeNode lchild) {
  451. this.lchild = lchild;
  452. }
  453. public TreeNode getRchild() {
  454. return rchild;
  455. }
  456. public void setRchild(TreeNode rchild) {
  457. this.rchild = rchild;
  458. }
  459. @Override
  460. public String toString() {
  461. return "TreeNode [data=" + data + ", bf=" + bf + ", lchild=" + lchild + ", rchild=" + rchild + "]";
  462. }
  463. }


运行结果



代码所示 平衡二叉树的构建过程



右平衡各种情况图(类似左平衡)


参考文章:http://www.cnblogs.com/javaminer/p/3462468.html

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

闽ICP备14008679号