1. public class AVLMap<K, V> implements Iterable<AVLEntry<K, V>> {
  2. private int size;
  3. private AVLEntry<K, V> root;
  4. private Comparator<K> comp;
  5. private LinkedList<AVLEntry<K, V>> stack = new LinkedList<AVLEntry<K, V>>();
  6.  
  7. private int compare(K a, K b) {
  8. if (comp != null) {
  9. return comp.compare(a, b);//有比较器就用比较器比较
  10. } else {
  11. Comparable<K> c = (Comparable<K>) a;
  12. return c.compareTo(b);//没有比较器就用对象本身比较
  13. }
  14. }
  15.  
  16. public AVLMap(Comparator<K> comp) {
  17. super();
  18. this.comp = comp;
  19. }
  20.  
  21. public AVLMap() {
  22. super();
  23. }
  24.  
  25. public int size() {
  26. return size;
  27. }
  28.  
  29. public boolean isEmpty() {
  30. return size == 0 ? true : false;
  31. }
  32.  
  33. public V put(K key, V value) {//存放节点,并把相比较过了的节点加入到stack。
  34. if (root == null) {//第一个元素是根
  35. root = new AVLEntry<K, V>(key, value);
  36. stack.push(root);
  37. size++;
  38. } else {
  39. AVLEntry<K, V> p = root;//每次从根节点开始比较,stack存放依次比较路上的所有节点,
  40. while (p != null) {
  41. stack.push(p); //调整二叉树时候用
  42. int compareResult = compare(key, p.key);//-1:<,key<p.key
  43. if (compareResult == 0) {
  44. p.setValue(value);//相等就覆盖
  45. break;
  46. } else if (compareResult < 0) {//小于0就跟左节点比较,左节点入栈,
  47. if (p.left == null) {
  48. p.left = new AVLEntry<K, V>(key, value);
  49. System.out.println(p.left);
  50. size++;
  51. stack.push(p.left);//刚刚加入的节点也入栈
  52. break;
  53. } else {// 还有左子树
  54. p = p.left;
  55. }
  56. } else {
  57. if (p.right == null) {//大于0就跟右节点比较,右节点入栈,
  58. p.right = new AVLEntry<K, V>(key, value);
  59. size++;
  60. stack.push(p.right);//刚刚加入的节点也入栈
  61. break;
  62. } else {// 还有右子树
  63. p = p.right;
  64. }
  65. }
  66. }
  67. }
  68. fixAfterInsertion(key);//修改依次比较路上的所有节点的高度,
  69. return value;
  70. }
  71.  
  72. @Override
  73. public Iterator<AVLEntry<K, V>> iterator() {
  74. return new AVLIterator<K, V>(root);
  75. }
  76.  
  77. private AVLEntry<K, V> getEntry(K key) {
  78. AVLEntry<K, V> p = root;
  79. while (p != null) {
  80. int compareResult = compare(key, p.key);
  81. if (compareResult == 0) {
  82. return p;
  83. } else if (compareResult < 0) {
  84. p = p.left;
  85. } else {
  86. p = p.right;
  87. }
  88. }
  89. return null;
  90. }
  91.  
  92. public boolean containsKey(K key) {
  93. AVLEntry<K, V> p = getEntry(key);
  94. return p != null;
  95. }
  96.  
  97. public V get(K key) {
  98. AVLEntry<K, V> p = getEntry(key);
  99. return p != null ? p.getValue() : null;
  100. }
  101.  
  102. public boolean containsValue(V value) {
  103. Iterator<AVLEntry<K, V>> itr = this.iterator();
  104. while (itr.hasNext()) {
  105. if (itr.next().getValue().equals(value)) {
  106. return true;
  107. }
  108. }
  109. return false;
  110. }
  111.  
  112. public AVLEntry<K, V> getFirstEntry(AVLEntry<K, V> p) {
  113. if (p == null) {
  114. return null;
  115. }
  116. while (p.left != null) {
  117. p = p.left;
  118. }
  119. return p;
  120. }
  121.  
  122. public AVLEntry<K, V> getLastEntry(AVLEntry<K, V> p) {
  123. if (p == null) {
  124. return null;
  125. }
  126. while (p.right != null) {
  127. p = p.right;
  128. }
  129. return p;
  130. }
  131.  
  132. private AVLEntry<K, V> deleteEntry(AVLEntry<K, V> p, K key) {//从根节点开始删除,
  133. if (p == null) {
  134. return null;
  135. } else {
  136. int compareResult = compare(key, p.key);
  137. if (compareResult == 0) {//删除就是p节点,不是删除p节点,而是修改p节点,删除的是被替换的节点。
  138. if (p.left == null && p.right == null) {
  139. p = null;
  140. } else if (p.left != null && p.right == null) {
  141. p = p.left;
  142. } else if (p.left == null && p.right != null) {
  143. p = p.right;
  144. } else {//从右边和从左边开始是一样的。
  145. if ((size & 1) == 0) {//偶数
  146. AVLEntry<K, V> rightMin = getFirstEntry(p.right);//p.right最左边的节点,就是比p大但是最小的。
  147. p.key = rightMin.key;
  148. p.value = rightMin.value;//修改要删除节点p的k,v,
  149. AVLEntry<K, V> newRight = deleteEntry(p.right, p.key);//从p.right开始删除p.key
  150. p.right = newRight;//修改要删除节点p的right,
  151. } else {//奇数
  152. AVLEntry<K, V> leftMax = getLastEntry(p.left);//p.left最右的节点,就是小于p但是最大的,
  153. p.key = leftMax.key;
  154. p.value = leftMax.value;
  155. AVLEntry<K, V> newLeft = deleteEntry(p.left, p.key);//从p.left开始删除p.key
  156. p.left = newLeft;
  157. }
  158. }
  159. } else if (compareResult < 0) {
  160. AVLEntry<K, V> newLeft = deleteEntry(p.left, key);//从p.left开始删除,返回并修改新的顶点p.left
  161. p.left = newLeft;
  162. } else {
  163. AVLEntry<K, V> newRight = deleteEntry(p.right, key);//从p.right开始删除,返回并修改新的顶点p.right
  164. p.right = newRight;
  165. }
  166. p = fixAfterDeletion(p);
  167. return p;//返回的是根节点
  168. }
  169. }
  170.  
  171. public int getHeight(AVLEntry<K, V> p) {
  172. return p == null ? 0 : p.height;//p==null?0:Max(getHeight(p.left),getHeight(p.right))+1
  173. }
  174.  
  175. private AVLEntry<K, V> rotateRight(AVLEntry<K, V> p) {//向右旋转,返回现在的顶节点p.left
  176. AVLEntry<K, V> left = p.left;
  177. p.left = left.right;
  178. left.right = p;
  179. p.height = Math.max(getHeight(p.left), getHeight(p.right)) + 1;//重新计算高度
  180. left.height = Math.max(getHeight(left.left), p.height) + 1;//重新计算高度
  181. return left;
  182. }
  183.  
  184. private AVLEntry<K, V> rotateLeft(AVLEntry<K, V> p) {//向左旋转,返回现在的顶节点p.right
  185. AVLEntry<K, V> right = p.right;
  186. p.right = right.left;
  187. right.left = p;
  188. p.height = Math.max(getHeight(p.left), getHeight(p.right)) + 1;//重新计算高度
  189. right.height = Math.max(p.height, getHeight(right.right)) + 1;//重新计算高度
  190. return right;
  191. }
  192.  
  193. private AVLEntry<K, V> firstLeftThenRight(AVLEntry<K, V> p) {
  194. p.left = rotateLeft(p.left);//p.left.right为p.left
  195. p = rotateRight(p);
  196. return p;
  197. }
  198.  
  199. private AVLEntry<K, V> firstRightThenLeft(AVLEntry<K, V> p) {
  200. p.right = rotateRight(p.right);
  201. p = rotateLeft(p);
  202. return p;
  203. }
  204. //一路上比较的节点都要重新调整大小,因为新加进去的节点在这些节点的下面,所以这些节点都要重新调整高度。
  205. private void fixAfterInsertion(K key) {//从新插入的节点到根节点,依次修改比较路上的节点的高度,
  206. AVLEntry<K, V> p = root;//根
  207. while (!stack.isEmpty()) {
  208. p = stack.pop();//比较路上的所有节点,包括刚刚加进去的节点,第一次pop出来的是最外面的节点,
  209. //就是刚刚加进去的节点,高度是1,然后依次修改比较路上的节点的高度。不在比较路上的节点的高度不要修改。
  210. int newHeight = Math.max(getHeight(p.left), getHeight(p.right)) + 1;
  211. if (p.height > 1 && newHeight == p.height) {//节点p的高度没改变
  212. stack.clear();//那么p的上层节点高度也没改变
  213. return;
  214. }
  215. p.height = newHeight;//修改高度
  216. int d = getHeight(p.left) - getHeight(p.right);
  217. if (Math.abs(d) <= 1) {//绝对值
  218. continue;
  219. } else {//左右子树高度差>=2,每次只增加一个节点,所以高度差从2开始,不会大于2,等于2就开始旋转调整。
  220. if (d == 2) {//p左边比右边高2个,
  221. //新插入的节点在p.left的左边,直接p右旋转,
  222. if (compare(key, p.left.key) < 0) {
  223. p = rotateRight(p);//返回现在的顶节点
  224. } else {//新插入的节点在p.left的右边,先把p.left左旋转,
  225. p = firstLeftThenRight(p);
  226. }
  227. } else {//-2,p右边比左边高2个,
  228. if (compare(key, p.right.key) > 0) {//p.right的右边直接左旋转即可。
  229. p = rotateLeft(p);//返回现在的顶节点
  230. } else {//先p.right右旋转再p左旋转。
  231. p = firstRightThenLeft(p);
  232. }
  233. }
  234. if (!stack.isEmpty()) {//peek()不移除元素
  235. if (compare(key, stack.peek().key) < 0) {//表示在节点stack.peek()的左边
  236. stack.peek().left = p;//就设置左边
  237. } else {
  238. stack.peek().right = p;
  239. }
  240. }
  241. }
  242. }
  243. root = p;//修改根节点
  244. }
  245.  
  246. public void checkBalance() {//断言AVL树的平衡性
  247. postOrderCheckBalance(root);
  248. }
  249.  
  250. private void postOrderCheckBalance(AVLEntry<K, V> p) {
  251. if (p != null) {
  252. postOrderCheckBalance(p.left);
  253. postOrderCheckBalance(p.right);
  254. Assert.assertTrue(Math.abs(getHeight(p.left) - getHeight(p.right)) <= 1);
  255. }
  256. }
  257.  
  258. public V remove(K key) {
  259. AVLEntry<K, V> entry = getEntry(key);
  260. if (entry == null) {
  261. return null;
  262. }
  263. V oldValue = entry.getValue();
  264. root = deleteEntry(root, key);//从根节点开始删除。删除路上的所有节点都要重新调整为平衡二叉树。
  265. size--;
  266. return oldValue;
  267. }
  268.  
  269. public void levelOrder() {
  270. Queue<AVLEntry<K, V>> queue = new LinkedList<AVLEntry<K, V>>();
  271. queue.offer(root);
  272. int preCount = 1;
  273. int pCount = 0;
  274. while (!queue.isEmpty()) {
  275. preCount--;
  276. AVLEntry<K, V> p = queue.poll();
  277. System.out.print(p + " ");
  278. if (p.left != null) {
  279. queue.offer(p.left);
  280. pCount++;
  281. }
  282. if (p.right != null) {
  283. queue.offer(p.right);
  284. pCount++;
  285. }
  286. if (preCount == 0) {
  287. preCount = pCount;
  288. pCount = 0;
  289. System.out.println();
  290. }
  291. }
  292. }
  293.  
  294. public AVLEntry<K, V> fixAfterDeletion(AVLEntry<K, V> p) {
  295. if (p == null) {
  296. return null;
  297. } else {
  298. p.height = Math.max(getHeight(p.left), getHeight(p.right)) + 1;
  299. int d = getHeight(p.left) - getHeight(p.right);
  300. if (d == 2) {//左节点比右节点高2个,不可能超过2个,一次只加进去一个节点,影响高度是1,是2的时候就已经调整了。
  301. if (getHeight(p.left.left) - getHeight(p.left.right) >= 0) {
  302. p = rotateRight(p);//左边>=右边高可以直接右旋转p。
  303. } else {
  304. p = firstLeftThenRight(p);//右边比左边高,就要先把p.left左旋转,然后在右旋转p。
  305. }
  306. } else if (d == -2) {//右节点比左节点高2个
  307. if (getHeight(p.right.right) - getHeight(p.right.left) >= 0) {
  308. p = rotateLeft(p);
  309. } else {
  310. p = firstRightThenLeft(p);
  311. }
  312. }
  313. return p;
  314. }
  315. }
  316.  
  317. @SuppressWarnings({ "rawtypes", "unchecked", "unused" })
  318. public static void main(String[] args) {
  319.  
  320. AVLMap al = new AVLMap();
  321. al.put(5d, 55d);
  322. al.put(4d, 44d);
  323. al.put(3d, 33d);
  324. al.put(4.5d, 4.55d);
  325. al.put(8d, 88d);
  326. al.put(4.1d, 4.11d);
  327. al.put(9d, 99d);
  328. al.put(10d, 100d);
  329. al.put(6.0d, 6.0d);
  330. Iterator i = al.iterator();//[{k:4.5,L:{k:4.0,L:{k:3.0},R:{k:4.1}},R:{k:8.0L:{k:5.0},R:{k:9.0,R:{k:10.0}}}},{k:4.0,L:{k:3.0},R:{k:4.1}},{k:3.0}]
  331. while(i.hasNext()) {
  332. System.out.println(i.next().toString());
  333. }
  334. AVLEntry j = al.getEntry(4.5d);
  335. al.levelOrder();
  336. double o = (Double) al.remove(8d);
  337.  
  338. AVLMap<Person, Integer> map = new AVLMap<Person, Integer>(new Comparator<Person>() {
  339. public int compare(Person o1, Person o2) {
  340. return o2.id - o1.id;
  341. }
  342. });
  343. for (int i1 = 0; i1 < 16; i1++) {
  344. map.put(new Person(new Random().nextInt(16),
  345. "name" + new Random().nextInt(16)),
  346. new Random().nextInt(16));
  347. }
  348. Iterator<AVLEntry<Person, Integer>> itr = map.iterator();
  349. while (itr.hasNext()) {
  350. System.out.println(itr.next().getKey());
  351. }
  352.  
  353. }
  354. }
  1. public class AVLEntry<K, V> implements Map.Entry<K, V> {
  2. public K key;
  3. public V value;
  4. public AVLEntry<K, V> left;
  5. public AVLEntry<K, V> right;
  6. public int height = 1;//单个结点高度就是1
  7. }
  8. }
  1. public class AVLIterator<K, V> implements Iterator<AVLEntry<K, V>> {//遍歷器
  2.  
  3. private Stack<AVLEntry<K, V>> stack;
  4.  
  5. public AVLIterator(AVLEntry<K, V> root) {
  6. super();
  7. stack = new Stack<AVLEntry<K, V>>();
  8. addLeftPath(root);//從根開始,最左边都加进去。
  9. }
  10.  
  11. private void addLeftPath(AVLEntry<K, V> p) {
  12. while (p != null) {
  13. //p入栈时候,把他的所有子左节点都加里面。右节点不在里面,出来的时候把右节点加进去(这样右节点就在里面了), 加这个右节点时候同时把所有子左节点加进去。
  14. //从根开始,所谓的左节点都是入栈时候加进去的,谓的右节点都是出栈时候加进去的(加这个右节点时候也叫入栈,把所有子左节点加进去)。
  15. stack.push(p);
  16. p = p.left;
  17. }
  18. }
  19.  
  20. @Override
  21. public boolean hasNext() {
  22. return stack.isEmpty() ? false : true;
  23. }
  24.  
  25. @Override
  26. public AVLEntry<K, V> next() {
  27. AVLEntry<K, V> p = stack.pop();//pop()是要移除元素的。
  28. addLeftPath(p.right);//pop()出去时候把右节点加进去,加右节点时候把该节点的所有左节点加进去。
  29. return p;
  30. }
  31.  
  32. @Override
  33. public void remove() {
  34. throw new ConcurrentModificationException("Can not remove!");
  35. }
  36. }

AVLMap平衡二叉树的更多相关文章

  1. java——平衡二叉树 AVLTree、AVLMap、AVLSet

    平衡二叉树:对于任意一个节点,左子树和右子树的高度差不能超过1 package Date_pacage; import java.util.ArrayList; public class AVLTre ...

  2. Java 平衡二叉树和AVL

      与BST<> 进行对比 import java.util.ArrayList; import java.util.Collections; public class Main { pu ...

  3. 算法与数据结构(十一) 平衡二叉树(AVL树)

    今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...

  4. [LeetCode] Balanced Binary Tree 平衡二叉树

    Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary ...

  5. Java数据结构——平衡二叉树的平衡因子(转自牛客网)

    若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性.首先要找出插入新结点后失去平衡的最小子树根结点的指针.然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树.当失去平衡的最小子树被 ...

  6. 【数据结构】平衡二叉树—AVL树

    (百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...

  7. 平衡二叉树AVL删除

    平衡二叉树的插入过程:http://www.cnblogs.com/hujunzheng/p/4665451.html 对于二叉平衡树的删除采用的是二叉排序树删除的思路: 假设被删结点是*p,其双亲是 ...

  8. 平衡二叉树AVL插入

    平衡二叉树(Balancedbinary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskiiand Landis)于1962年首先提出的,所以又称为AVL树. 定义:平衡二叉树或为 ...

  9. 数据结构快速回顾——平衡二叉树 AVL (转)

    平衡二叉树(Balanced Binary Tree)是二叉查找树的一个进化体,也是第一个引入平衡概念的二叉树.1962年,G.M. Adelson-Velsky 和 E.M. Landis发明了这棵 ...

随机推荐

  1. 彻底搞懂B树、B+树、B*树、R 树

    出处:http://blog.csdn.net/v_JULY_v . 第一节.B树.B+树.B*树1.前言: 动态查找树主要有:二叉查找树(Binary Search Tree),平衡二叉查找树(Ba ...

  2. App过大

    最近开发中遇到一个报错信息 如下 Error:Cannot fit requested classes in a single dex file.Try supplying a main-dex li ...

  3. Android Xutils3 完全解析

    1.先来认识一下xUtils3 1)xUtils3简介 xUtils是基于Afinal开发的目前功能比较完善的一个Android开源框架,最近又发布了xUtil3.0,在增加新功能的同时又提高了框架的 ...

  4. Centos7允许使用密码登录

      现在使用云主机比较多,所以一般都是使用秘钥登录,当做一个集群的时候需要几台机器之间免密登录时,就需要修改他的配置文件了,刚做运维那会儿,很熟练,现在忘得差不多了,特此记录一下,下次又这个需求时就不 ...

  5. telnet: connect to address 192.168.120.32: No route to host

    原因是 防火墙没有开端口. telnet 测试 3306端口,报错 telnet: connect to address 192.168.120.32: No route to host 再次链接就可 ...

  6. Java多线程上下文切换

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10843676.html 一:什么是上下文切换 CPU处理任务时不是一直只处理一个,而是通过给每个线程分配CP ...

  7. HDP 3.1.0 集成 Sqoop2

    HDP 3.1.0 集成 Sqoop2 本文原始地址:https://sitoi.cn/posts/65261.html 环境 由三台主机组成的 HDP 3.1.0 集群 配置好时间同步 步骤 下载 ...

  8. centos7 下 yum 安装Nginx

    centos7 下 yum 安装和配置 Nginx 添加yum源 Nginx不在默认的yum源中,可以使用epel或者官网的yum源,这里使用官网的yum源 rpm -ivh http://nginx ...

  9. 201871010133-赵永军《面向对象程序设计(java)》第二周学习总结

    201871010133-赵永军<面向对象程序设计(java)>第二周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这 ...

  10. 2019南昌网络赛I:Yukino With Subinterval(CDQ) (树状数组套主席树)

    题意:询问区间有多少个连续的段,而且这段的颜色在[L,R]才算贡献,每段贡献是1. 有单点修改和区间查询. 思路:46min交了第一发树套树,T了. 稍加优化多交几次就过了. 不难想到,除了L这个点, ...