平衡二叉树(AVL树)
平衡二叉树(AVL树)
平衡二叉树简介:
平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1。常见的符合平衡树的有,B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等。
具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1, 并且左右两个子树都是-棵平衡二叉树。
平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。可以保证查询效率高。
举例看下下面AVL树的特点吧:左右两个子树的高度差的绝对值不超过1
第三棵树的左子树高度是3,右子树高度是1,3-1=2,所以第三个不是AVL树

AVL树左旋
AVL树左旋图解
要求: 给你一个数列,创建出对应的平衡二叉树.数列 {4,3,6,5,7,8}

AVL树左旋步骤:
- 创建一个新的节点,值为当前节点的值
- 把新节点的的左子树设置为原节点的左子树:4-->指向3
- 把新节点的右子树设置为当前节点的右子树的左子树
- 把当前节点的值:4 换成当前右子节点的值:6
- 把当前节点的右子树设为右子树的右子树
- 把当前节点的左子树设为新的节点:6-->指向4
AVL树的高度计算
核心:首先要把一棵树的高度以及左子树和右子树的高度计算出来
Node类中添加这三个方法:
1 /** 2 * 返回以当前节点为根节点的树的高度 3 * 4 * @return 返回树的高度 5 */ 6 public int heightTree() { 7 // 比较左子树跟右子树的高度,返回最大的。+1是因为树本身还要站一层 8 return Math.max(left == null ? 0 : left.heightTree(), right == null ? 0 : right.heightTree()) + 1; 9 } 10 11 /** 12 * 返回左子树的高度 13 * 14 * @return 左子树高度 15 */ 16 public int leftHeight() { 17 if (left == null) { 18 return 0; 19 } 20 return left.heightTree(); 21 } 22 23 /** 24 * 返回右子树的高度 25 * 26 * @return 右子树高度 27 */ 28 public int rightHeight() { 29 if (right == null) { 30 return 0; 31 } 32 return right.heightTree(); 33 }
AVLTree类:直接用上个二叉排序树的树代码即可!
1 class AVLTree { 2 private Node root; 3 4 public Node getRoot() { 5 return root; 6 } 7 8 /** 9 * 查找要删除的节点 10 * 11 * @param value 12 * @return 13 */ 14 public Node delSearch(int value) { 15 if (root == null) { 16 return null; 17 } else { 18 return root.delSearch(value); 19 } 20 } 21 22 /** 23 * 查找到要删除节点的父节点 24 * 25 * @param value 26 * @return 27 */ 28 public Node delSearchParent(int value) { 29 if (root == null) { 30 return null; 31 } else { 32 return root.delSearchParent(value); 33 } 34 } 35 36 /** 37 * 找到最小值并删除 38 * 39 * @param node 40 * @return 返回删除节点的值 41 */ 42 public int delRightTreeMin(Node node) { 43 // 作一个辅助节点 44 Node target = node; 45 // 循环往左子树进行查找,就会找到最小值 46 while (target.left != null) { 47 target = target.left; 48 } 49 // 删除最小值 50 delNode(target.value); 51 // 返回最小值 52 return target.value; 53 } 54 55 /** 56 * 删除节点 57 * 58 * @param value 59 */ 60 public void delNode(int value) { 61 if (root == null) { 62 return; 63 } else { 64 // 1、找到要删除的节点 65 Node targetNode = delSearch(value); 66 // 没有找到 67 if (targetNode == null) { 68 return; 69 } 70 // 表示这颗二叉排序树只有一个节点(父节点) 71 if (root.left == null && root.right == null) { 72 root = null; 73 return; 74 } 75 76 // 2、找到要删除节点的父节点 77 Node parentNode = delSearchParent(value); 78 // 表示要删除的节点是一个叶子节点 79 if (targetNode.left == null && targetNode.right == null) { 80 // 继续判断这个叶子节点是父节点的左子节点还是右子节点 81 if (parentNode.left != null && parentNode.left.value == value) { 82 // 将这个叶子节点置为空 83 parentNode.left = null; 84 } else if (parentNode.right != null && parentNode.right.value == value) { 85 parentNode.right = null; 86 } 87 } else if (targetNode.left != null && targetNode.right != null) {// 删除有两颗子树的节点 88 // 找到最小值 89 int minVal = delRightTreeMin(targetNode.right); 90 // 重置 91 targetNode.value = minVal; 92 } else {// 删除只有一颗子树的节点 93 if (targetNode.left != null) {// 如果要删除的节点有左子节点 94 if (parentNode != null) { 95 // 待删除节点是父节点的左子节点 96 if (parentNode.left.value == value) { 97 parentNode.left = targetNode.left; 98 } else {// 待删除节点是父节点的右子节点 99 parentNode.right = targetNode.left; 100 } 101 } else { 102 root = targetNode.left; 103 } 104 } else {// 如果要删除的节点有右子节点 105 if (parentNode != null) { 106 // 待删除节点是父节点的左子节点 107 if (parentNode.left.value == value) { 108 parentNode.left = targetNode.right; 109 } else {// 待删除节点是父节点的右子节点 110 parentNode.right = targetNode.right; 111 } 112 } else { 113 root = targetNode.right; 114 } 115 } 116 } 117 } 118 } 119 120 /** 121 * 添加节点的方法 122 * 123 * @param node 124 */ 125 public void addNode(Node node) { 126 // root节点为空,就让root成为根节点 127 if (root == null) { 128 root = node; 129 } else {// root节点不为空,就继续向树中添加节点 130 root.addNode(node); 131 } 132 } 133 134 /** 135 * 进行中序遍历 136 */ 137 public void infixOrder() { 138 if (root != null) { 139 root.infixOrder(); 140 } else { 141 System.out.println("二叉树为空,无法进行排序!"); 142 } 143 } 144 }
测试树的高度:
public static void main(String[] args) {
int[] arr = {4, 3, 6, 5, 7, 8};
// 创建AVL树对象
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
// 添加节点
avlTree.addNode(new Node(arr[i]));
}
// 中序遍历
System.out.println("======================中序遍历======================");
avlTree.infixOrder();
System.out.println("======================没有平衡处理前======================");
System.out.println("没有平衡处理前树的高度:" + avlTree.getRoot().heightTree());
System.out.println("没有平衡处理前树的左子树高度:" + avlTree.getRoot().leftHeight());
System.out.println("没有平衡处理前树的右子树高度:" + avlTree.getRoot().rightHeight());
}

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

AVL树右旋
AVL树右旋图解

AVL树右旋代码:
在Node类中添加平衡二叉树右旋代码:
/**
* AVL树右旋转的方法
*/
public void rightHand() {
// 1.创建新的节点,值为当前节点的值
Node newNode = new Node(this.value);
// 2.把新节点的右子树设置为当前节点的右子树
newNode.right = this.right;
// 3.把新节点的左子树设为当前节点的左子树的右子树
newNode.left = this.left.right;
// 4.把当前节点的值换成当前节点左子节点的值
this.value = this.left.value;
// 5.把当前节点的左子树设为左子树的左子树
this.left = this.left.left;
// 6.把当前节点的右子树设置为新的节点
this.right = newNode;
}
在Node类中添加节点时候增加判断判断节点应该左旋还是右旋:
1 /** 2 * 二叉排序树的添加 3 * 4 * @param node 5 */ 6 public void addNode(Node node) { 7 if (node == null) { 8 return; 9 } 10 // 判断传进来的节点值与当前节点的关系 11 if (node.value < this.value) { 12 // 左子节点为null 13 if (this.left == null) { 14 this.left = node; 15 } else { 16 // 递归的向左子树添加节点 17 this.left.addNode(node); 18 } 19 } else { // 传入的节点大于等于当前节点 20 if (this.right == null) { 21 this.right = node; 22 } else { 23 // 递归的向右子树添加节点 24 this.right.addNode(node); 25 } 26 } 27 // 当添加完成一个节点后:(右子树的高度-左子树的高度)>1,进行左旋操作 28 if (rightHeight() - leftHeight() > 1) { 29 // 进行左旋操作 30 leftHand(); 31 } 32 // 当添加完成一个节点后:(左子树的高度-右子树的高度)>1,进行右旋操作 33 if (leftHeight() - rightHeight() > 1) { 34 // 进行左旋操作 35 rightHand(); 36 } 37 }
测试:
public static void main(String[] args) {
// 测试左旋转
//int[] arr = {4, 3, 6, 5, 7, 8};
// 测试右旋转
int[] arr = {10, 12, 8, 9, 7, 6};
// 创建AVL树对象
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
// 添加节点
avlTree.addNode(new Node(arr[i]));
}
// 中序遍历
/*System.out.println("======================中序遍历======================");
avlTree.infixOrder();
System.out.println("======================AVL树左旋平衡处理======================");
System.out.println("左旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
System.out.println("左旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
System.out.println("左旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());*/
System.out.println("======================中序遍历======================");
avlTree.infixOrder();
System.out.println("======================AVL树右旋平衡处理======================");
System.out.println("右旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
System.out.println("右旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
System.out.println("右旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());
System.out.println("右旋平衡处理后树的根节点的值:"+avlTree.getRoot().value);
}

AVL树双旋
AVL树双旋问题以及图解:
右旋遇到的问题:
前面的两个数列,进行单旋转(即一次旋转)就可以将非平衡二叉树转成平衡二叉树,但是在某些情况下,单旋转不能完成平衡二叉树的转换。
比如数列int[] arr = { 10, 11, 7, 6, 8, 9 }; 运行原来的代码可以看到,并没有转成 AVL 树.
public static void main(String[] args) {
// 测试左旋转
//int[] arr = {4, 3, 6, 5, 7, 8};
// 测试右旋转
//int[] arr = {10, 12, 8, 9, 7, 6};
// 测试旋转
int[] arr = { 10, 11, 7, 6, 8, 9 };
// 创建AVL树对象
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
// 添加节点
avlTree.addNode(new Node(arr[i]));
}
// 中序遍历
/*System.out.println("======================中序遍历======================");
avlTree.infixOrder();
System.out.println("======================AVL树左旋平衡处理======================");
System.out.println("左旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
System.out.println("左旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
System.out.println("左旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());*/
System.out.println("======================中序遍历======================");
avlTree.infixOrder();
System.out.println("======================AVL树右旋平衡处理======================");
System.out.println("右旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
System.out.println("右旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
System.out.println("右旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());
System.out.println("右旋平衡处理后树的根节点的值:" + avlTree.getRoot().value);
}
可以发现,将数列进行二叉排序树后的右旋处理,并没有变成AVL数:(3 - 1) > 1 所以说不满足AVL树


解决思路:
- 当符号右旋转的条件时
- 如果它的左子树的右子树高度大于它的左子树的高度
- 先对当前这个结点的左节点进行左旋转
- 在对当前结点进行右旋转的操作即可
代码实现:
在添加节点的时候增加判断,直接上代码了:
/** * 二叉排序树的添加 * * @param node */ public void addNode(Node node) { if (node == null) { return; } // 判断传进来的节点值与当前节点的关系 if (node.value < this.value) { // 左子节点为null if (this.left == null) { this.left = node; } else { // 递归的向左子树添加节点 this.left.addNode(node); } } else { // 传入的节点大于等于当前节点 if (this.right == null) { this.right = node; } else { // 递归的向右子树添加节点 this.right.addNode(node); } } // 当添加完成一个节点后:(右子树的高度-左子树的高度)>1,进行左旋操作 if (rightHeight() - leftHeight() > 1) { // 左旋之双旋判断 if (this.right != null && this.right.rightHeight() > this.right.rightHeight()) { // 先对右子结点进行右旋转 this.right.rightHand(); // 再进行左旋操作 leftHand(); } else { // 直接进行左旋操作 leftHand(); } // 右旋判断: 当添加完成一个节点后:(左子树的高度-右子树的高度)>1,进行右旋操作 } else if (leftHeight() - rightHeight() > 1) { // 右旋之双旋判断 if (this.left != null && this.left.rightHeight() > this.left.leftHeight()) { // 先对当前结点的左结点(左子树)->左旋转 this.left.leftHand(); // 再进行右旋操作 rightHand(); } else { // 直接进行右旋操作 rightHand(); } } }
测试:

总体代码:
package Demo11_平衡二叉树_AVL树;
/**
* @author zhangzhixi
* @date 2021/3/12 22:58
*/
public class AVLTreeDemo {
public static void main(String[] args) {
// 测试左旋转
//int[] arr = {4, 3, 6, 5, 7, 8};
// 测试右旋转
//int[] arr = {10, 12, 8, 9, 7, 6};
// 测试旋转
int[] arr = {10, 11, 7, 6, 8, 9};
// 创建AVL树对象
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
// 添加节点
avlTree.addNode(new Node(arr[i]));
}
// 中序遍历
/*System.out.println("======================中序遍历======================");
avlTree.infixOrder();
System.out.println("======================AVL树左旋平衡处理======================");
System.out.println("左旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
System.out.println("左旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
System.out.println("左旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());*/
System.out.println("======================中序遍历======================");
avlTree.infixOrder();
System.out.println("======================AVL树右旋平衡处理======================");
System.out.println("右旋平衡处理后树的高度:" + avlTree.getRoot().heightTree());
System.out.println("右旋平衡处理后树的左子树高度:" + avlTree.getRoot().leftHeight());
System.out.println("右旋平衡处理后树的右子树高度:" + avlTree.getRoot().rightHeight());
System.out.println("右旋平衡处理后树的根节点的值:" + avlTree.getRoot().value);
}
}
class AVLTree {
private Node root;
public Node getRoot() {
return root;
}
/**
* 查找要删除的节点
*
* @param value
* @return
*/
public Node delSearch(int value) {
if (root == null) {
return null;
} else {
return root.delSearch(value);
}
}
/**
* 查找到要删除节点的父节点
*
* @param value
* @return
*/
public Node delSearchParent(int value) {
if (root == null) {
return null;
} else {
return root.delSearchParent(value);
}
}
/**
* 找到最小值并删除
*
* @param node
* @return 返回删除节点的值
*/
public int delRightTreeMin(Node node) {
// 作一个辅助节点
Node target = node;
// 循环往左子树进行查找,就会找到最小值
while (target.left != null) {
target = target.left;
}
// 删除最小值
delNode(target.value);
// 返回最小值
return target.value;
}
/**
* 删除节点
*
* @param value
*/
public void delNode(int value) {
if (root == null) {
return;
} else {
// 1、找到要删除的节点
Node targetNode = delSearch(value);
// 没有找到
if (targetNode == null) {
return;
}
// 表示这颗二叉排序树只有一个节点(父节点)
if (root.left == null && root.right == null) {
root = null;
return;
}
// 2、找到要删除节点的父节点
Node parentNode = delSearchParent(value);
// 表示要删除的节点是一个叶子节点
if (targetNode.left == null && targetNode.right == null) {
// 继续判断这个叶子节点是父节点的左子节点还是右子节点
if (parentNode.left != null && parentNode.left.value == value) {
// 将这个叶子节点置为空
parentNode.left = null;
} else if (parentNode.right != null && parentNode.right.value == value) {
parentNode.right = null;
}
} else if (targetNode.left != null && targetNode.right != null) {// 删除有两颗子树的节点
// 找到最小值
int minVal = delRightTreeMin(targetNode.right);
// 重置
targetNode.value = minVal;
} else {// 删除只有一颗子树的节点
if (targetNode.left != null) {// 如果要删除的节点有左子节点
if (parentNode != null) {
// 待删除节点是父节点的左子节点
if (parentNode.left.value == value) {
parentNode.left = targetNode.left;
} else {// 待删除节点是父节点的右子节点
parentNode.right = targetNode.left;
}
} else {
root = targetNode.left;
}
} else {// 如果要删除的节点有右子节点
if (parentNode != null) {
// 待删除节点是父节点的左子节点
if (parentNode.left.value == value) {
parentNode.left = targetNode.right;
} else {// 待删除节点是父节点的右子节点
parentNode.right = targetNode.right;
}
} else {
root = targetNode.right;
}
}
}
}
}
/**
* 添加节点的方法
*
* @param node
*/
public void addNode(Node node) {
// root节点为空,就让root成为根节点
if (root == null) {
root = node;
} else {// root节点不为空,就继续向树中添加节点
root.addNode(node);
}
}
/**
* 进行中序遍历
*/
public void infixOrder() {
if (root != null) {
root.infixOrder();
} else {
System.out.println("二叉树为空,无法进行排序!");
}
}
}
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
/**
* 返回以当前节点为根节点的树的高度
*
* @return 返回树的高度
*/
public int heightTree() {
// 比较左子树跟右子树的高度,返回最大的。+1是因为树本身还要站一层
return Math.max(left == null ? 0 : left.heightTree(), right == null ? 0 : right.heightTree()) + 1;
}
/**
* 返回左子树的高度
*
* @return 左子树高度
*/
public int leftHeight() {
if (left == null) {
return 0;
}
return left.heightTree();
}
/**
* 返回右子树的高度
*
* @return 右子树高度
*/
public int rightHeight() {
if (right == null) {
return 0;
}
return right.heightTree();
}
/**
* 查找到要删除的节点
*
* @param value 希望删除节点的值
* @return 找到了就返回这个要删除的节点,没有找到就返回null
*/
public Node delSearch(int value) {
// 找到的就是要删除的节点
if (value == this.value) {
return this;
} else if (value < this.value) {
/**向左子节点查找*/
if (this.left == null) {
return null;
}
// 继续递归查找
return this.left.delSearch(value);
} else {// 要删除节点的值是大于等于当前节点的值
if (this.right == null) {
return null;
}
return this.right.delSearch(value);
}
}
/**
* 查找到要删除节点的父节点
*
* @param value 要删除的节点的值
* @return 找到就返回要删除节点的父节点,没有找到就返回null
*/
public Node delSearchParent(int value) {
// 如果当前节点就是要删除的节点的父节点,就返回
if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
return this;
} else {
// 满足条件表示向左递归查找
if (this.left != null && value < this.value) {
// 向左子树递归找到就返回
return this.left.delSearchParent(value);
} else if (this.right != null && value >= this.value) {
// 向右子树递归找到就返回
return this.right.delSearchParent(value);
} else {
// 没有找到要删除节点的父节点
return null;
}
}
}
/**
* 二叉排序树的添加
*
* @param node
*/
public void addNode(Node node) {
if (node == null) {
return;
}
// 判断传进来的节点值与当前节点的关系
if (node.value < this.value) {
// 左子节点为null
if (this.left == null) {
this.left = node;
} else {
// 递归的向左子树添加节点
this.left.addNode(node);
}
} else { // 传入的节点大于等于当前节点
if (this.right == null) {
this.right = node;
} else {
// 递归的向右子树添加节点
this.right.addNode(node);
}
}
// 当添加完成一个节点后:(右子树的高度-左子树的高度)>1,进行左旋操作
if (rightHeight() - leftHeight() > 1) {
// 左旋之双旋判断
if (this.right != null && this.right.rightHeight() > this.right.rightHeight()) {
// 先对右子结点进行右旋转
this.right.rightHand();
// 再进行左旋操作
leftHand();
} else {
// 直接进行左旋操作
leftHand();
}
// 右旋判断: 当添加完成一个节点后:(左子树的高度-右子树的高度)>1,进行右旋操作
} else if (leftHeight() - rightHeight() > 1) {
// 右旋之双旋判断
if (this.left != null && this.left.rightHeight() > this.left.leftHeight()) {
// 先对当前结点的左结点(左子树)->左旋转
this.left.leftHand();
// 再进行右旋操作
rightHand();
} else {
// 直接进行右旋操作
rightHand();
}
}
}
/**
* 二叉排序树的中序遍历
*/
void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}
/**
* AVL树左旋转的方法
*/
public void leftHand() {
// 1.创建新的节点,值为当前节点的值
Node newNode = new Node(this.value);
// 2.把新节点的左子树设置为当前节点的左子树
newNode.left = this.left;
// 3.把新节点的右子树设为当前节点的右子树的左子树
newNode.right = this.right.left;
// 4.把当前节点的值换成当前节点右子节点的值
this.value = this.right.value;
// 5.把当前节点的右子树设为右子树的右子树
this.right = this.right.right;
// 6.把当前节点的左子树设置为新的节点
this.left = newNode;
}
/**
* AVL树右旋转的方法
*/
public void rightHand() {
// 1.创建新的节点,值为当前节点的值
Node newNode = new Node(this.value);
// 2.把新节点的右子树设置为当前节点的右子树
newNode.right = this.right;
// 3.把新节点的左子树设为当前节点的左子树的右子树
newNode.left = this.left.right;
// 4.把当前节点的值换成当前节点左子节点的值
this.value = this.left.value;
// 5.把当前节点的左子树设为左子树的左子树
this.left = this.left.left;
// 6.把当前节点的右子树设置为新的节点
this.right = newNode;
}
}

浙公网安备 33010602011771号