Java-AVL树

x33g5p2x  于2022-02-07 转载在 Java  
字(7.6k)|赞(0)|评价(0)|浏览(407)

在线可视化AVL树演示

https://www.cs.usfca.edu/~galles/visualization/AVLtree.html

AVL树简介

AVL树其实就是二叉查找树,不同的是在二叉查找树的基础上进行了加强,弥补了二叉树会有可能退化为链表情况

AVL的核心原理就是自璇,自动调整树的高度,使树始终保存接近完全二叉树的结构,这样就能最大化利用二叉树查找树的查询特性完成高效处理,但是缺点也是很明显增加和删除就会慢上很多

AVL树必要条件:

条件一:它必须是二叉查找树。
条件二:每个节点的左子树和右子树的高度最多为1,否则就要进行自璇来调整

AVL树相关概念

平衡因子

将二叉树上节点的左子树高度减去右子树高度的值称为该节点的平衡因子BF(Balance Factor)。一共有三种正常情况(0 ,1,-1),只要平衡因子不是这三种情况那么就是需要进行树的平衡了 ,值大于1那么就是树左节点深了,需要进行右旋,如果小于-1那么就是右节点深了,需要左旋,当然不止这两种可能,一共有四种可能性下面会详细介绍的

右单旋

]

左单旋

双旋-左右

先小节点进行左旋

然后在进行大节点的右旋

双旋-右左

先小节点进行右旋

然后在进行大节点的右旋

经验之谈

我刚接触这个avl树的时候一脸懵逼,树怎么旋转?,节点直接怎么交换呢?
然后我就去看数据结构书,和看大神文章发现还是没卵用,总是是懂非懂的,但是直到有一天,一个电闪雷鸣的夜里,我下定决看图看代码,看图看代码,看图看代码,然后终于领悟了,最关键的地方,就是怎么处理树的高度,让我们知道树不平衡和树是平衡的,之后在对应不同的场景使用不同的旋转策略就行了

代码实现

使用递归实现AVL方式,最为简单,其他方式都太复杂了不好理解也不好写,并且效率也没有递归快

  1. package com.tree.avltree;
  2. /**
  3. * AVL树
  4. *
  5. * @author huanmin
  6. */
  7. public class AVLTree<T extends Comparable<T>> {
  8. private AVLTreeNode<T> mRoot; // 根结点
  9. // AVL树的节点(内部类)
  10. class AVLTreeNode<T extends Comparable<T>> {
  11. T key; // 关键字(键值)
  12. int height; // 高度
  13. AVLTreeNode<T> left; // 左孩子
  14. AVLTreeNode<T> right; // 右孩子
  15. public AVLTreeNode(T key, AVLTreeNode<T> left, AVLTreeNode<T> right) {
  16. this.key = key;
  17. this.left = left;
  18. this.right = right;
  19. this.height = 0;
  20. }
  21. }
  22. // 构造函数
  23. public AVLTree() {
  24. mRoot = null;
  25. }
  26. /*
  27. * 获取树的高度
  28. */
  29. private int height(AVLTreeNode<T> tree) {
  30. if (tree != null) {
  31. return tree.height;
  32. }
  33. return 0;
  34. }
  35. public int height() {
  36. return height(mRoot);
  37. }
  38. /*
  39. * 比较两个值的大小
  40. */
  41. private int max(int a, int b) {
  42. return a>b ? a : b;
  43. }
  44. /*
  45. * 前序遍历"AVL树"
  46. */
  47. private void preOrder(AVLTreeNode<T> tree) {
  48. if(tree != null) {
  49. System.out.print(tree.key+" ");
  50. preOrder(tree.left);
  51. preOrder(tree.right);
  52. }
  53. }
  54. public void preOrder() {
  55. preOrder(mRoot);
  56. }
  57. /*
  58. * 中序遍历"AVL树"
  59. */
  60. private void inOrder(AVLTreeNode<T> tree) {
  61. if(tree != null)
  62. {
  63. inOrder(tree.left);
  64. System.out.print(tree.key+" ");
  65. inOrder(tree.right);
  66. }
  67. }
  68. public void inOrder() {
  69. inOrder(mRoot);
  70. }
  71. /*
  72. * 后序遍历"AVL树"
  73. */
  74. private void postOrder(AVLTreeNode<T> tree) {
  75. if(tree != null) {
  76. postOrder(tree.left);
  77. postOrder(tree.right);
  78. System.out.print(tree.key+" ");
  79. }
  80. }
  81. public void postOrder() {
  82. postOrder(mRoot);
  83. }
  84. /*
  85. * (递归实现)查找"AVL树x"中键值为key的节点
  86. */
  87. private AVLTreeNode<T> search(AVLTreeNode<T> x, T key) {
  88. if (x==null) {
  89. return x;
  90. }
  91. int cmp = key.compareTo(x.key);
  92. if (cmp < 0) {
  93. return search(x.left, key);
  94. } else if (cmp > 0) {
  95. return search(x.right, key);
  96. } else {
  97. return x;
  98. }
  99. }
  100. public AVLTreeNode<T> search(T key) {
  101. return search(mRoot, key);
  102. }
  103. /*
  104. * (非递归实现)查找"AVL树x"中键值为key的节点
  105. */
  106. private AVLTreeNode<T> iterativeSearch(AVLTreeNode<T> x, T key) {
  107. while (x!=null) {
  108. int cmp = key.compareTo(x.key);
  109. if (cmp < 0) {
  110. x = x.left;
  111. } else if (cmp > 0) {
  112. x = x.right;
  113. } else {
  114. return x;
  115. }
  116. }
  117. return x;
  118. }
  119. public AVLTreeNode<T> iterativeSearch(T key) {
  120. return iterativeSearch(mRoot, key);
  121. }
  122. /*
  123. * 查找最小结点:返回tree为根结点的AVL树的最小结点。
  124. */
  125. private AVLTreeNode<T> minimum(AVLTreeNode<T> tree) {
  126. if (tree == null) {
  127. return null;
  128. }
  129. while(tree.left != null) {
  130. tree = tree.left;
  131. }
  132. return tree;
  133. }
  134. public T minimum() {
  135. AVLTreeNode<T> p = minimum(mRoot);
  136. if (p != null) {
  137. return p.key;
  138. }
  139. return null;
  140. }
  141. /*
  142. * 查找最大结点:返回tree为根结点的AVL树的最大结点。
  143. */
  144. private AVLTreeNode<T> maximum(AVLTreeNode<T> tree) {
  145. if (tree == null) {
  146. return null;
  147. }
  148. while(tree.right != null) {
  149. tree = tree.right;
  150. }
  151. return tree;
  152. }
  153. public T maximum() {
  154. AVLTreeNode<T> p = maximum(mRoot);
  155. if (p != null) {
  156. return p.key;
  157. }
  158. return null;
  159. }
  160. /*
  161. * LL:左左对应的情况(左单旋转)。
  162. *
  163. * 返回值:旋转后的根节点
  164. */
  165. private AVLTreeNode<T> leftLeftRotation(AVLTreeNode<T> k2) {
  166. AVLTreeNode<T> k1;
  167. k1 = k2.left;
  168. k2.left = k1.right;
  169. k1.right = k2;
  170. k2.height = max( height(k2.left), height(k2.right)) + 1;
  171. k1.height = max( height(k1.left), k2.height) + 1;
  172. return k1;
  173. }
  174. /*
  175. * RR:右右对应的情况(右单旋转)。
  176. *
  177. * 返回值:旋转后的根节点
  178. */
  179. private AVLTreeNode<T> rightRightRotation(AVLTreeNode<T> k1) {
  180. AVLTreeNode<T> k2;
  181. k2 = k1.right;
  182. k1.right = k2.left;
  183. k2.left = k1;
  184. k1.height = max( height(k1.left), height(k1.right)) + 1;
  185. k2.height = max( height(k2.right), k1.height) + 1;
  186. return k2;
  187. }
  188. /*
  189. * LR:左右对应的情况(左双旋转)。
  190. *
  191. * 返回值:旋转后的根节点
  192. */
  193. private AVLTreeNode<T> leftRightRotation(AVLTreeNode<T> k3) {
  194. k3.left = rightRightRotation(k3.left);
  195. return leftLeftRotation(k3);
  196. }
  197. /*
  198. * RL:右左对应的情况(右双旋转)。
  199. *
  200. * 返回值:旋转后的根节点
  201. */
  202. private AVLTreeNode<T> rightLeftRotation(AVLTreeNode<T> k1) {
  203. k1.right = leftLeftRotation(k1.right);
  204. return rightRightRotation(k1);
  205. }
  206. /*
  207. * 将结点插入到AVL树中,并返回根节点
  208. *
  209. * 参数说明:
  210. * tree AVL树的根结点
  211. * key 插入的结点的键值
  212. * 返回值:
  213. * 根节点
  214. */
  215. private AVLTreeNode<T> insert(AVLTreeNode<T> tree, T key) {
  216. if (tree == null) {
  217. // 新建节点
  218. tree = new AVLTreeNode<T>(key, null, null);
  219. } else {
  220. //key大于tree.key那么 返回1 小于返回-1 等于返回0
  221. int cmp = key.compareTo(tree.key);
  222. if (cmp < 0) { // 应该将key插入到"tree的左子树"的情况
  223. tree.left = insert(tree.left, key);
  224. // 插入节点后,若AVL树失去平衡,则进行相应的调节。
  225. if (height(tree.left) - height(tree.right) == 2) {
  226. //(左子树太深了)进行调整
  227. if (key.compareTo(tree.left.key) < 0) {
  228. //单旋
  229. tree = leftLeftRotation(tree);
  230. } else {
  231. //双选
  232. tree = leftRightRotation(tree);
  233. }
  234. }
  235. } else if (cmp > 0) { // 应该将key插入到"tree的右子树"的情况
  236. tree.right = insert(tree.right, key);
  237. // 插入节点后,若AVL树失去平衡,则进行相应的调节。
  238. if (height(tree.right) - height(tree.left) == 2) {
  239. //(右子树太深了)进行调整
  240. if (key.compareTo(tree.right.key) > 0) {
  241. //单旋
  242. tree = rightRightRotation(tree);
  243. } else {
  244. //双选
  245. tree = rightLeftRotation(tree);
  246. }
  247. }
  248. } else { // cmp==0 解决办法可以添加链表的方式,这里就不演示了
  249. System.out.println("添加失败:不允许添加相同的节点!");
  250. }
  251. }
  252. //在递归进入时候对每一个节点高度+1 ,递归返归时候,拿上个节点的值+1
  253. tree.height = max( height(tree.left), height(tree.right)) + 1;
  254. return tree;
  255. }
  256. public void insert(T key) {
  257. mRoot = insert(mRoot, key);
  258. }
  259. /*
  260. * 删除结点(z),返回根节点
  261. *
  262. * 参数说明:
  263. * tree AVL树的根结点
  264. * z 待删除的结点
  265. * 返回值:
  266. * 根节点
  267. */
  268. private AVLTreeNode<T> remove(AVLTreeNode<T> tree, AVLTreeNode<T> z) {
  269. // 根为空 或者 没有要删除的节点,直接返回null。
  270. if (tree==null || z==null) {
  271. return null;
  272. }
  273. int cmp = z.key.compareTo(tree.key);
  274. if (cmp < 0) { // 待删除的节点在"tree的左子树"中
  275. tree.left = remove(tree.left, z);
  276. // 删除节点后,若AVL树失去平衡,则进行相应的调节。
  277. if (height(tree.right) - height(tree.left) == 2) {
  278. AVLTreeNode<T> r = tree.right;
  279. if (height(r.left) > height(r.right)) {
  280. tree = rightLeftRotation(tree);
  281. } else {
  282. tree = rightRightRotation(tree);
  283. }
  284. }
  285. } else if (cmp > 0) { // 待删除的节点在"tree的右子树"中
  286. tree.right = remove(tree.right, z);
  287. // 删除节点后,若AVL树失去平衡,则进行相应的调节。
  288. if (height(tree.left) - height(tree.right) == 2) {
  289. AVLTreeNode<T> l = tree.left;
  290. if (height(l.right) > height(l.left)) {
  291. tree = leftRightRotation(tree);
  292. } else {
  293. tree = leftLeftRotation(tree);
  294. }
  295. }
  296. } else { // tree是对应要删除的节点。
  297. // tree的左右孩子都非空
  298. if ((tree.left!=null) && (tree.right!=null)) {
  299. if (height(tree.left) > height(tree.right)) {
  300. // 如果tree的左子树比右子树高;
  301. // 则(01)找出tree的左子树中的最大节点
  302. // (02)将该最大节点的值赋值给tree。
  303. // (03)删除该最大节点。
  304. // 这类似于用"tree的左子树中最大节点"做"tree"的替身;
  305. // 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。
  306. AVLTreeNode<T> max = maximum(tree.left);
  307. tree.key = max.key;
  308. tree.left = remove(tree.left, max);
  309. } else {
  310. // 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1)
  311. // 则(01)找出tree的右子树中的最小节点
  312. // (02)将该最小节点的值赋值给tree。
  313. // (03)删除该最小节点。
  314. // 这类似于用"tree的右子树中最小节点"做"tree"的替身;
  315. // 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的。
  316. AVLTreeNode<T> min = maximum(tree.right);
  317. tree.key = min.key;
  318. tree.right = remove(tree.right, min);
  319. }
  320. } else {
  321. AVLTreeNode<T> tmp = tree;
  322. tree = (tree.left!=null) ? tree.left : tree.right;
  323. tmp = null;
  324. }
  325. }
  326. return tree;
  327. }
  328. public void remove(T key) {
  329. AVLTreeNode<T> z;
  330. if ((z = search(mRoot, key)) != null) {
  331. mRoot = remove(mRoot, z);
  332. }
  333. }
  334. /*
  335. * 销毁AVL树
  336. */
  337. private void destroy(AVLTreeNode<T> tree) {
  338. if (tree==null) {
  339. return ;
  340. }
  341. if (tree.left != null) {
  342. destroy(tree.left);
  343. }
  344. if (tree.right != null) {
  345. destroy(tree.right);
  346. }
  347. tree = null;
  348. }
  349. public void destroy() {
  350. destroy(mRoot);
  351. }
  352. /*
  353. * 打印"二叉查找树"
  354. *
  355. * key -- 节点的键值
  356. * direction -- 0,表示该节点是根节点;
  357. * -1,表示该节点是它的父结点的左孩子;
  358. * 1,表示该节点是它的父结点的右孩子。
  359. */
  360. private void print(AVLTreeNode<T> tree, T key, int direction) {
  361. if(tree != null) {
  362. if(direction==0) // tree是根节点
  363. {
  364. System.out.printf("%2d is root\n", tree.key, key);
  365. } else // tree是分支节点
  366. {
  367. System.out.printf("%2d is %2d's %6s child\n", tree.key, key, direction==1?"right" : "left");
  368. }
  369. print(tree.left, tree.key, -1);
  370. print(tree.right,tree.key, 1);
  371. }
  372. }
  373. public void print() {
  374. if (mRoot != null) {
  375. print(mRoot, mRoot.key, 0);
  376. }
  377. }
  378. }

点赞 -收藏-关注-便于以后复习和收到最新内容有其他问题在评论区讨论-或者私信我-收到会在第一时间回复如有侵权,请私信联系我感谢,配合,希望我的努力对你有帮助^_^

相关文章

最新文章

更多