1. package com.atguigu.avl;
    2. public class AVLTreeDemo {
    3. public static void main(String[] args) {
    4. //int[] arr = {4,3,6,5,7,8};
    5. //int[] arr = { 10, 12, 8, 9, 7, 6 };
    6. int[] arr = { 10, 11, 7, 6, 8, 9 };
    7. //创建一个 AVLTree对象
    8. AVLTree avlTree = new AVLTree();
    9. //添加结点
    10. for(int i=0; i < arr.length; i++) {
    11. avlTree.add(new Node(arr[i]));
    12. }
    13. //遍历
    14. System.out.println("中序遍历");
    15. avlTree.infixOrder();
    16. System.out.println("在平衡处理~~");
    17. System.out.println("树的高度=" + avlTree.getRoot().height()); //3
    18. System.out.println("树的左子树高度=" + avlTree.getRoot().leftHeight()); // 2
    19. System.out.println("树的右子树高度=" + avlTree.getRoot().rightHeight()); // 2
    20. System.out.println("当前的根结点=" + avlTree.getRoot());//8
    21. }
    22. }
    23. // 创建AVLTree
    24. class AVLTree {
    25. private Node root;
    26. public Node getRoot() {
    27. return root;
    28. }
    29. // 查找要删除的结点
    30. public Node search(int value) {
    31. if (root == null) {
    32. return null;
    33. } else {
    34. return root.search(value);
    35. }
    36. }
    37. // 查找父结点
    38. public Node searchParent(int value) {
    39. if (root == null) {
    40. return null;
    41. } else {
    42. return root.searchParent(value);
    43. }
    44. }
    45. // 编写方法:
    46. // 1. 返回的 以node 为根结点的二叉排序树的最小结点的值
    47. // 2. 删除node 为根结点的二叉排序树的最小结点
    48. /**
    49. *
    50. * @param node
    51. * 传入的结点(当做二叉排序树的根结点)
    52. * @return 返回的 以node 为根结点的二叉排序树的最小结点的值
    53. */
    54. public int delRightTreeMin(Node node) {
    55. Node target = node;
    56. // 循环的查找左子节点,就会找到最小值
    57. while (target.left != null) {
    58. target = target.left;
    59. }
    60. // 这时 target就指向了最小结点
    61. // 删除最小结点
    62. delNode(target.value);
    63. return target.value;
    64. }
    65. // 删除结点
    66. public void delNode(int value) {
    67. if (root == null) {
    68. return;
    69. } else {
    70. // 1.需求先去找到要删除的结点 targetNode
    71. Node targetNode = search(value);
    72. // 如果没有找到要删除的结点
    73. if (targetNode == null) {
    74. return;
    75. }
    76. // 如果我们发现当前这颗二叉排序树只有一个结点
    77. if (root.left == null && root.right == null) {
    78. root = null;
    79. return;
    80. }
    81. // 去找到targetNode的父结点
    82. Node parent = searchParent(value);
    83. // 如果要删除的结点是叶子结点
    84. if (targetNode.left == null && targetNode.right == null) {
    85. // 判断targetNode 是父结点的左子结点,还是右子结点
    86. if (parent.left != null && parent.left.value == value) { // 是左子结点
    87. parent.left = null;
    88. } else if (parent.right != null && parent.right.value == value) {// 是由子结点
    89. parent.right = null;
    90. }
    91. } else if (targetNode.left != null && targetNode.right != null) { // 删除有两颗子树的节点
    92. int minVal = delRightTreeMin(targetNode.right);
    93. targetNode.value = minVal;
    94. } else { // 删除只有一颗子树的结点
    95. // 如果要删除的结点有左子结点
    96. if (targetNode.left != null) {
    97. if (parent != null) {
    98. // 如果 targetNode 是 parent 的左子结点
    99. if (parent.left.value == value) {
    100. parent.left = targetNode.left;
    101. } else { // targetNode 是 parent 的右子结点
    102. parent.right = targetNode.left;
    103. }
    104. } else {
    105. root = targetNode.left;
    106. }
    107. } else { // 如果要删除的结点有右子结点
    108. if (parent != null) {
    109. // 如果 targetNode 是 parent 的左子结点
    110. if (parent.left.value == value) {
    111. parent.left = targetNode.right;
    112. } else { // 如果 targetNode 是 parent 的右子结点
    113. parent.right = targetNode.right;
    114. }
    115. } else {
    116. root = targetNode.right;
    117. }
    118. }
    119. }
    120. }
    121. }
    122. // 添加结点的方法
    123. public void add(Node node) {
    124. if (root == null) {
    125. root = node;// 如果root为空则直接让root指向node
    126. } else {
    127. root.add(node);
    128. }
    129. }
    130. // 中序遍历
    131. public void infixOrder() {
    132. if (root != null) {
    133. root.infixOrder();
    134. } else {
    135. System.out.println("二叉排序树为空,不能遍历");
    136. }
    137. }
    138. }
    139. // 创建Node结点
    140. class Node {
    141. int value;
    142. Node left;
    143. Node right;
    144. public Node(int value) {
    145. this.value = value;
    146. }
    147. // 返回左子树的高度
    148. public int leftHeight() {
    149. if (left == null) {
    150. return 0;
    151. }
    152. return left.height();
    153. }
    154. // 返回右子树的高度
    155. public int rightHeight() {
    156. if (right == null) {
    157. return 0;
    158. }
    159. return right.height();
    160. }
    161. // 返回 以该结点为根结点的树的高度
    162. public int height() {
    163. return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
    164. }
    165. //左旋转方法
    166. private void leftRotate() {
    167. //创建新的结点,以当前根结点的值
    168. Node newNode = new Node(value);
    169. //把新的结点的左子树设置成当前结点的左子树
    170. newNode.left = left;
    171. //把新的结点的右子树设置成带你过去结点的右子树的左子树
    172. newNode.right = right.left;
    173. //把当前结点的值替换成右子结点的值
    174. value = right.value;
    175. //把当前结点的右子树设置成当前结点右子树的右子树
    176. right = right.right;
    177. //把当前结点的左子树(左子结点)设置成新的结点
    178. left = newNode;
    179. }
    180. //右旋转
    181. private void rightRotate() {
    182. Node newNode = new Node(value);
    183. newNode.right = right;
    184. newNode.left = left.right;
    185. value = left.value;
    186. left = left.left;
    187. right = newNode;
    188. }
    189. // 查找要删除的结点
    190. /**
    191. *
    192. * @param value
    193. * 希望删除的结点的值
    194. * @return 如果找到返回该结点,否则返回null
    195. */
    196. public Node search(int value) {
    197. if (value == this.value) { // 找到就是该结点
    198. return this;
    199. } else if (value < this.value) {// 如果查找的值小于当前结点,向左子树递归查找
    200. // 如果左子结点为空
    201. if (this.left == null) {
    202. return null;
    203. }
    204. return this.left.search(value);
    205. } else { // 如果查找的值不小于当前结点,向右子树递归查找
    206. if (this.right == null) {
    207. return null;
    208. }
    209. return this.right.search(value);
    210. }
    211. }
    212. // 查找要删除结点的父结点
    213. /**
    214. *
    215. * @param value
    216. * 要找到的结点的值
    217. * @return 返回的是要删除的结点的父结点,如果没有就返回null
    218. */
    219. public Node searchParent(int value) {
    220. // 如果当前结点就是要删除的结点的父结点,就返回
    221. if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
    222. return this;
    223. } else {
    224. // 如果查找的值小于当前结点的值, 并且当前结点的左子结点不为空
    225. if (value < this.value && this.left != null) {
    226. return this.left.searchParent(value); // 向左子树递归查找
    227. } else if (value >= this.value && this.right != null) {
    228. return this.right.searchParent(value); // 向右子树递归查找
    229. } else {
    230. return null; // 没有找到父结点
    231. }
    232. }
    233. }
    234. @Override
    235. public String toString() {
    236. return "Node [value=" + value + "]";
    237. }
    238. // 添加结点的方法
    239. // 递归的形式添加结点,注意需要满足二叉排序树的要求
    240. public void add(Node node) {
    241. if (node == null) {
    242. return;
    243. }
    244. // 判断传入的结点的值,和当前子树的根结点的值关系
    245. if (node.value < this.value) {
    246. // 如果当前结点左子结点为null
    247. if (this.left == null) {
    248. this.left = node;
    249. } else {
    250. // 递归的向左子树添加
    251. this.left.add(node);
    252. }
    253. } else { // 添加的结点的值大于 当前结点的值
    254. if (this.right == null) {
    255. this.right = node;
    256. } else {
    257. // 递归的向右子树添加
    258. this.right.add(node);
    259. }
    260. }
    261. //当添加完一个结点后,如果: (右子树的高度-左子树的高度) > 1 , 左旋转
    262. if(rightHeight() - leftHeight() > 1) {
    263. //如果它的右子树的左子树的高度大于它的右子树的右子树的高度
    264. if(right != null && right.leftHeight() > right.rightHeight()) {
    265. //先对右子结点进行右旋转
    266. right.rightRotate();
    267. //然后在对当前结点进行左旋转
    268. leftRotate(); //左旋转..
    269. } else {
    270. //直接进行左旋转即可
    271. leftRotate();
    272. }
    273. return ; //必须要!!!
    274. }
    275. //当添加完一个结点后,如果 (左子树的高度 - 右子树的高度) > 1, 右旋转
    276. if(leftHeight() - rightHeight() > 1) {
    277. //如果它的左子树的右子树高度大于它的左子树的高度
    278. if(left != null && left.rightHeight() > left.leftHeight()) {
    279. //先对当前结点的左结点(左子树)->左旋转
    280. left.leftRotate();
    281. //再对当前结点进行右旋转
    282. rightRotate();
    283. } else {
    284. //直接进行右旋转即可
    285. rightRotate();
    286. }
    287. }
    288. }
    289. // 中序遍历
    290. public void infixOrder() {
    291. if (this.left != null) {
    292. this.left.infixOrder();
    293. }
    294. System.out.println(this);
    295. if (this.right != null) {
    296. this.right.infixOrder();
    297. }
    298. }
    299. }