对比二叉树
二叉树的搜索时间复杂度是log(n),但是在某些条件由于二叉树的不平衡很容易退化成链表,导致时间复杂度从O(log(n))变成O(n)。
AVL树则在二叉树的基础上添加了平衡功能,确保不会在插入和删除中导致节点失去平衡,时间复杂度稳定保持在O(log(n))。
添加节点失去平衡
| LL | 在A的左子树根节点的左子树上插入节点而破坏平衡 | 右旋转 | 
|---|---|---|
| RR | 在A的右子树根节点的右子树上插入节点而破坏平衡 | 左旋转 | 
| LR | 在A的右子树根节点的右子树上插入节点而破坏平衡 | 先左旋后右旋 | 
| RL | 在A的右子树根节点的左子树上插入节点而破坏平衡 | 先右旋后左旋 | 
- 左子树高: 右旋
 - 右子树高: 左旋
 - 
删除节点失去平衡
从AVL树中删除,可以通过把要删除的节点向下旋转成一个叶子节点,接着直接移除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费固定的时间,所以删除处理在整体上耗费O(log n) 时间。
目前实现的二叉树的删除和AVL树的删除操作是一样的,都是找到删除节点,然后交换成叶子节点,删除.
 删除叶子节点
- 删除只有一颗子树的节点
- 只有左子树
 - 只有右子树
 
 - 删除存在2颗子树的节点
 
AVL可视化
WIKI
https://zh.wikipedia.org/wiki/AVL%E6%A0%91#%E5%88%A0%E9%99%A4
AVL树
package io.github.chenshun00.web.tree;/*** @author chenshun00@gmail.com* @since 2021/8/14 10:26 上午*/public class AVLTree {private Node root;public static void main(String[] args) {AVLTree avlTree = new AVLTree();avlTree.insert(1);avlTree.insert(2);avlTree.insert(3);avlTree.insert(4);avlTree.insert(5);avlTree.insert(6);avlTree.insert(7);avlTree.insert(8);avlTree.insert(9);avlTree.insert(999);// avlTree.traverse();// System.out.println(avlTree.isAVLTree());// final Node node = avlTree.findNode(9);// System.out.println(node);avlTree.delete(999);System.out.println("删除节点4===>" + avlTree.delete(4));// avlTree.traverse();System.out.println("删除节点3===>" + avlTree.delete(3));System.out.println("删除节点6===>" + avlTree.delete(6));System.out.println("删除节点1===>" + avlTree.delete(1));avlTree.traverse();}public boolean isAVLTree() {if (root == null) {return false;}return avl(root, true);}public Node findNode(int data) {return root == null ? null : doFindNode(data);}//删除操作public boolean delete(int data) {//找节点,找不到直接返回final Node node = findNode(data);if (node == null) {return false;}//如果节点是rootif (node == root) {//如果root是叶子if (isLeaf(node)) {root = null;} else {//如果root还有子树replace(node);}return true;}//被移除的是叶子节点if (isLeaf(node)) {//找到叶子节点的parent节点,并且说明当前节点是左(left)子树还是右子树(right)final Node parent = node.parent;if (parent.leftChild == node) {parent.leftChild = null;} else {parent.rightChild = null;}//重新平衡reBalance(parent);} else {replace(node);}return true;}/*** 插入** @param data 数据* @return {@link Node}*/public Node insert(int data) {if (root == null) {root = new Node(null, null, null, data, 1);return root;}return doInsert(data, root);}public void traverse() {doTraverse(root);System.out.println();}//==============================删================================private boolean isLeaf(Node node) {return node == null || node.leftChild == null && node.rightChild == null;}/*** 从AVL树中删除,可以通过把要删除的节点向下旋转成一个叶子节点,接着直接移除这个叶子节点来完成。* 因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费固定的时间,所以删除处理在整体上耗费O(log n) 时间。*/private void replace(Node node) {//找到左子树中最大的节点 或者 是右子树中最小的节点final Node next = findNext(node);final Node parent = next.parent;//修改被替换节点的指向if (parent.leftChild == next) {if (next.leftChild == null) {parent.leftChild = null;} else {parent.leftChild = next.leftChild;next.leftChild.parent = parent;}} else if (parent.rightChild == next) {if (next.rightChild == null) {parent.rightChild = null;} else {parent.rightChild = next.rightChild;next.rightChild.parent = parent;}}//节点修改数据node.data = next.data;reBalance(node);}private Node findNext(Node node) {if (node.leftChild != null) {return doFindMaxNode(node.leftChild);}return doFindMinNode(node.rightChild);}private Node doFindMaxNode(Node node) {if (node.rightChild == null) {return node;} else {return doFindMaxNode(node.rightChild);}}private Node doFindMinNode(Node node) {if (node.leftChild == null) {return node;} else {return doFindMinNode(node.leftChild);}}//==============================查==================================private Node doFindNode(Integer data) {return data >= root.data ? findRight(root, data) : findLeft(root, data);}private Node findRight(Node root, Integer data) {if (root == null) {return null;}if (root.data.equals(data)) {return root;}if (data >= root.data) {//右子树return findRight(root.rightChild, data);} else {//左子树return findLeft(root.leftChild, data);}}private Node findLeft(Node root, Integer data) {if (root == null) {return null;}if (root.data.equals(data)) {return root;}if (data >= root.data) {//右子树return findRight(root.rightChild, data);} else {//左子树return findLeft(root.leftChild, data);}}//===========================是否AVL树===========================private boolean avl(Node node, boolean ok) {if (!ok) return false;if (node == null) {return true;}final boolean balance = isBalance(node);return avl(node.rightChild, balance) && avl(node.leftChild, balance);}//===========================插入===========================private Node doInsert(int data, Node parent) {//如果数据比当前节点小,则插入作为左子树if (data < parent.data) {if (parent.leftChild == null) {parent.leftChild = new Node(parent, null, null, data, 1);//修改高度 && 可能需要重新修正为平衡树changeNodeHeight(parent.leftChild);return parent.leftChild;} else {//存在左子的情况下,继续找左子return doInsert(data, parent.leftChild);}} else {//否则插入作为右子树if (parent.rightChild == null) {parent.rightChild = new Node(parent, null, null, data, 1);//修改高度 && 可能需要重新修正为平衡树changeNodeHeight(parent.rightChild);return parent.rightChild;} else {//存在右子的情况下,继续找右子return doInsert(data, parent.rightChild);}}}private void doReBalance(Node node) {if (isBalance(node)) {return;}node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;if (leftNotBalance(node)) {//平衡因子,用来判断是旋转一次,还是旋转两次final int rotationFactor = rotationFactor(node.leftChild);if (rotationFactor < 0) {rr(node, node == root);} else {rl(node.leftChild, node == root);}} else {//右节点失去平衡 平衡因子,用来判断是旋转一次,还是旋转两次final int rotationFactor = rotationFactor(node.rightChild);if (rotationFactor > 0) {ll(node, node == root);} else {lr(node.rightChild, node == root);}}}/*** 重新平衡*/private void reBalance(Node node) {//如果是root节点需要重新平衡if (node.parent == null) {doReBalance(node);return;}node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;Node parent = node.parent;do {//递归修改parent节点的高度parent.height = Math.max(getHeight(parent.leftChild), getHeight(parent.rightChild)) + 1;//查看节点是否失去平衡if (!isBalance(parent)) {//左节点失去平衡if (leftNotBalance(parent)) {//平衡因子,用来判断是旋转一次,还是旋转两次final int rotationFactor = rotationFactor(parent.leftChild);if (rotationFactor < 0) {rr(parent, parent == root);} else {rl(parent.leftChild, parent == root);}} else {//右节点失去平衡 平衡因子,用来判断是旋转一次,还是旋转两次final int rotationFactor = rotationFactor(parent.rightChild);if (rotationFactor > 0) {ll(parent, parent == root);} else {lr(parent.rightChild, parent == root);}}}parent = parent.parent;} while (parent != null);}private void changeNodeHeight(Node node) {Node parent = node.parent;do {//递归修改parent节点的高度parent.height = Math.max(getHeight(parent.leftChild), getHeight(parent.rightChild)) + 1;//查看节点是否失去平衡if (!isBalance(parent)) {//左节点失去平衡(右旋)if (leftNotBalance(parent)) {//平衡因子(左子失去平衡,需要进行右旋,使用当前节点的左子树的根节点来判断平衡因子)//用来判断是旋转一次,还是旋转两次,如果小于0则进行一次右旋即可//否则进行一次左旋,一次右旋,左旋的作用是将数据规整为右旋的规则作用。方便做右旋操作final int rotationFactor = rotationFactor(parent.leftChild);if (rotationFactor < 0) {//右旋rr(parent, parent == root);} else {//rl(parent.leftChild, parent == root);}} else {//右节点失去平衡 平衡因子,用来判断是旋转一次,还是旋转两次 (左旋)//同上变的注释final int rotationFactor = rotationFactor(parent.rightChild);if (rotationFactor > 0) {ll(parent, parent == root);} else {lr(parent.rightChild, parent == root);}}}parent = parent.parent;} while (parent != null);}//先右旋 再左旋private void rl(Node node, boolean b) {final Node parent = node.parent;parent.leftChild = node.rightChild;node.rightChild.parent = parent;node.parent = parent.leftChild;node.rightChild = parent.rightChild == null ? null :parent.rightChild.leftChild;parent.leftChild.leftChild = node;node.height = Math.max(getHeight(null), getHeight(node.rightChild)) + 1;node.parent.height = Math.max(getHeight(node.parent), getHeight(node.parent)) + 1;rr(parent, b);}//先左旋 再右旋private void lr(Node node, boolean b) {final Node parent = node.parent;parent.rightChild = node.leftChild;node.leftChild.parent = parent;node.parent = parent.rightChild;node.leftChild = parent.leftChild == null ? null : parent.rightChild.rightChild;parent.rightChild.rightChild = node;node.height = Math.max(getHeight(null), getHeight(node.rightChild)) + 1;node.parent.height = Math.max(getHeight(node.parent), getHeight(node.parent)) + 1;ll(parent, b);}/*** 旋转因子,用来判断是旋转一次,还是旋转2次** @param node 节点* @return int*/private int rotationFactor(Node node) {final int left = getHeight(node.leftChild);final int right = getHeight(node.rightChild);return right - left;}/*** 左子树不平衡** @param node 节点* @return boolean*/private boolean leftNotBalance(Node node) {return (getHeight(node.rightChild) - getHeight(node.leftChild)) < 0;}private boolean rightBalance(Node node) {return (getHeight(node.rightChild) - getHeight(node.leftChild)) > 0;}/*** 判断当前节点是否平衡,abs(左子和右子的节点高度)<=1即可** @param node 节点* @return boolean*/private boolean isBalance(Node node) {if (node == null) {return true;}final int lh = getHeight(node.leftChild);final int rh = getHeight(node.rightChild);return Math.abs(rh - lh) <= 1;}//===========================遍历===========================private void doTraverse(Node node) {if (node == null) {System.out.println("么数据");return;}if (node.leftChild != null) {doTraverse(node.leftChild);}System.out.print(node.data + "(" + node.height + ")" + "\t");if (node.rightChild != null) {doTraverse(node.rightChild);}}//===========================旋转===========================/*** 右旋(某节点的左子树不平衡),注释同左旋* RightRight的缩写*/private void rr(Node node, boolean isRoot) {final Node parent = node.parent;boolean isRightChild = false;if (parent != null) {if (parent.rightChild == node) {isRightChild = true;}}final Node leftChild = node.leftChild;//设置node.leftChild = leftChild.rightChild;if (leftChild.rightChild != null) {leftChild.rightChild.parent = node;}leftChild.parent = node.parent;if (parent != null) {if (isRightChild) {node.parent.rightChild = leftChild;} else {node.parent.leftChild = leftChild;}}leftChild.rightChild = node;node.parent = leftChild;if (isRoot) {root = leftChild;}node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;leftChild.height = Math.max(getHeight(leftChild.leftChild), getHeight(leftChild.rightChild)) + 1;}/*** 左旋转(右子树不平衡) if是用来兼容各种节点有数据的情况* 假设当前的根节点为5,右子树的参考节点数据为 6 7,新插入节点为8的情况,* 6由于插入变为不平衡节点,需要旋转成根为7,左子为6,右子为8的情况** <code>* 5 5 5* / \ / \ /\* 3 6 ====> 3 6 ====> 3 7* / \ / \ / / \* 1 7 1 7 1 6 8* \* 8* </code>** @param node 6 失去平衡的节点*/private void ll(Node node, boolean isRoot) {//获取当前节点的parent节点,用来修改parent的leftChild使用,因为一开始的leftChild旋转作为rightChild了//parent == 5final Node parent = node.parent;//判断当前节点是parent的左节点还是右节点,下文需要使用//如果当前节点是parent的左节点,旋转产生的新"根节点" 挂到parent的左子上//如果当前节点是parent的右节点,旋转产生的新"根节点" 挂到parent的右子上boolean isRightChild = false;if (parent != null) {if (parent.rightChild == node) {//6是5节点的右子树的根节点isRightChild = true;}}//获取右节点7final Node rightChild = node.rightChild;//将节点6的右孩子设置为7的左孩子,需要明白旋转方式,这里是旋转的理论基础node.rightChild = rightChild.leftChild;//如果节点7的左孩子不为空,将左孩子的parent指向6,这里完成了旋转的第一轮交接if (rightChild.leftChild != null) {rightChild.leftChild.parent = node;}//节点7的parent应用修改为节点6的parent5//节点5就指向了节点7rightChild.parent = node.parent;//如果parent为空,需要旋转出来的节点挂到parent节点下//如果6节点是左子,挂5的左子,如果6是右子就挂5的右子if (parent != null) {if (isRightChild) {node.parent.rightChild = rightChild;} else {node.parent.leftChild = rightChild;}}//节点7的左子设置为节点6rightChild.leftChild = node;//节点的6的parent指向节点7node.parent = rightChild;//这里的root判断是,由于Java的值传递if (isRoot) {root = rightChild;}//重新设置节点高度node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;rightChild.height = Math.max(getHeight(rightChild.leftChild), getHeight(rightChild.rightChild)) + 1;}/*** 获取节点高度** @param node 节点* @return int*/private int getHeight(Node node) {return node == null ? 0 : node.height;}}
Node 类
package io.github.chenshun00.web.tree;/*** @author luobo.cs@raycloud.com* @since 2021/8/14 10:26 上午*/public class Node {public Node(Node parent, Node leftChild, Node rightChild, Integer data, Integer height) {this.leftChild = leftChild;this.rightChild = rightChild;this.parent = parent;this.data = data;this.height = height;}public Node leftChild;public Node rightChild;public Node parent;public Integer data;public Integer height;@Overridepublic String toString() {return "Node:" + data;}}
