上一节中介绍并实现了二叉搜索树,它有一个明显缺陷,当按某种顺序插入Node构建的树高度接近N,那么增删查的时间复杂度退化为O(N),AVL可以有效的弥补这方面缺陷,简单来说AVL在每次完成BST的增删操作后,会对树结构进行旋转以维持树的高度在。
树的高度
以Node为根的树的高度等于Node到每个叶子的最大值,规定null的高度为0,一个Node的高度为1,三个Node组成的满二叉树高度为2,以此类推。
//以该Node为根的树高度
public static int getHeight(Node node) {
if (node == null) return 0;
return Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;
}
不平衡Node
如果一个Node的左子树高度与右子树高度相差绝对值大于一,则认为该Node是不平衡的。
//从当前Node向上查找第一个不平衡Node
public static Node unbalancedNode(Node node) {
if(node == null)return null;
do {
if (Math.abs(getHeight(node.leftChild) - getHeight(node.rightChild)) > 1)
return node;
node = node.getParent();
} while (node != null);
return null;
}
旋转操作
旋转是AVL维持树高度的核心方法,对于一个Node向父Node方向查找的第一个不平衡Node(UnbalancedNode),如果引起它不平衡的Leaf位于左Node的左子树则对UnbalancedNode使用LL旋转,如果该Leaf位于右Node的右子树则对UnbalancedNode使用RR旋转,同理还有LR旋转与RL旋转。
更新后的Node类
Node与BST中的Node几乎一致,不过此次增加两种操作,第一种操作是返回以该Node为根的树高度;第二种操作是从该Node沿父Node方向查找第一不平衡的Node。
package TreeStructure.BinaryTree;
import org.jetbrains.annotations.NotNull;
public class Node<T extends Comparable<T>> implements Comparable<Node<T>> {
private T data;
//父Node
private Node<T> parent;
//左子Node
private Node<T> leftChild;
//右子Node
private Node<T> rightChild;
public Node(T data) {
this.data = data;
}
public Node(T data, Node<T> parent) {
this.data = data;
this.parent = parent;
}
@Override
public int compareTo(@NotNull Node<T> tNode) {
return this.data.compareTo(tNode.getData());
}
public void visit() {
//节点的访问动作写在这里
// System.out.println("Parent:" + (this.parent == null ? "null" : this.parent.data));
System.out.println("Node:" + data);
// System.out.println("Left Child:" + (this.leftChild == null ? "null" : this.leftChild.data));
// System.out.println("Right Child:" + (this.rightChild == null ? "null" : this.rightChild.data));
// System.out.println();
}
//判断该Node是否有子Node
public boolean hasChild() {
return this.getLeftChild() != null || this.getRightChild() != null;
}
public boolean isLeaf() {
return this.getLeftChild() == null && this.getRightChild() == null;
}
public boolean hasLeftChild() {
return this.getLeftChild() != null;
}
public boolean hasRightChild() {
return this.getRightChild() != null;
}
//判断该Node是否为parentBinaryNode的左子Node
public boolean isLeftChildOf(Node<T> parentNode) {
if (parentNode != null && parentNode.getLeftChild() == this) return true;
return false;
}
public boolean isRightChildOf(Node<T> parentNode) {
if (parentNode != null && parentNode.getRightChild() == this) return true;
return false;
}
public Node<T> getParent() {
return parent;
}
//返回Node右子树中的最小值
public Node<T> getSuccessor() {
if (rightChild == null) return null;
Node<T> current = rightChild;
while (current.hasLeftChild()) current = current.getLeftChild();
return current;
}
public void setParent(Node<T> parent) {
this.parent = parent;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Node<T> getLeftChild() {
return leftChild;
}
public void setLeftChild(Node<T> leftChild) {
this.leftChild = leftChild;
}
public Node<T> getRightChild() {
return rightChild;
}
public void setRightChild(Node<T> rightChild) {
this.rightChild = rightChild;
}
//从当前Node向上查找第一个不平衡Node
public static Node unbalancedNode(Node node) {
if(node == null)return null;
do {
if (Math.abs(getHeight(node.leftChild) - getHeight(node.rightChild)) > 1)
return node;
node = node.getParent();
} while (node != null);
return null;
}
//以该Node为根的树高度
public static int getHeight(Node node) {
if (node == null) return 0;
return Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;
}
}
AVL类(继承自BST类)
插入操作
AVL的插入操作可以先按照BST的方法插入,然后寻找插入Node的父Node方向的第一不平衡Node,对该UnbalancedNode完成旋转,因此需要让BST的插入方法返回该插入Node
删除操作
AVL的删除操作可以先按照BST的方法删除,如果删除的Node没有或只有一个子Node,那么删除Node的父Node可能出现不平衡,因此需要从删除Node的父Node向上查找UnbalancedNode,如果删除Node含有两个Child,那么我们实际删除的Node是Successor因此要返回Successor的父Node
更新后的BST类
package TreeStructure.BinaryTree;
import org.jetbrains.annotations.NotNull;
public class BST<T extends Comparable<T>> extends BinaryTree<T> {
@Override
public Node<T> insert(T key) {
if (root == null) {
//树上没有Node直接把root设置为插入Node
root = new Node<T>(key);
return root;
} else {
//插入Node的父Node
Node<T> parent;
//遍历Node
Node<T> current = root;
do {
parent = current;
if (current.getData().compareTo(key) > 0) current = current.getLeftChild();
//相等的Node插入右子树
else current = current.getRightChild();
} while (current != null);
current = new Node<T>(key, parent);
//此时Parent的子Node是null,null用插入Node替代
if (parent.getData().compareTo(key) > 0) parent.setLeftChild(current);
else parent.setRightChild(current);
return current;
}
}
@Override
public Node<T> delete(T key) {
//待删除Node
Node<T> delete = find(key);
if (delete == null) return null;
if (delete.hasChild())
if (delete.hasLeftChild() && delete.hasRightChild())
//delete有两个Child
return deleteNodeWithTwoChild(delete);
else
//delete有一个Child
return deleteNodeWithOneChild(delete);
else
//delete没有Child
return deleteNodeWithoutChild(delete);
}
protected Node<T> deleteNodeWithoutChild(@NotNull Node<T> delete) {
//delete没有子Node(叶子Node)
if (delete == root) return root = null;
else {
//delete的父Node
Node<T> parent = delete.getParent();
if (delete.isLeftChildOf(parent)) parent.setLeftChild(null);
else parent.setRightChild(null);
return parent;
}
}
protected Node<T> deleteNodeWithOneChild(@NotNull Node<T> delete) {
//delete有一个Child
if (delete == root) {
//delete是root
if (root.hasLeftChild()) root = root.getLeftChild();
else root = root.getRightChild();
root.setParent(null);
return null;
} else {
//delete的父Node
Node<T> parent = delete.getParent();
if (delete.isLeftChildOf(parent)) {
if (delete.hasLeftChild()) {
parent.setLeftChild(delete.getLeftChild());
delete.getLeftChild().setParent(parent);
} else {
parent.setLeftChild(delete.getRightChild());
delete.getRightChild().setParent(parent);
}
} else {
if (delete.hasLeftChild()) {
parent.setRightChild(delete.getLeftChild());
delete.getLeftChild().setParent(parent);
} else {
parent.setRightChild(delete.getRightChild());
delete.getRightChild().setParent(parent);
}
}
return parent;
}
}
private Node<T> deleteNodeWithTwoChild(@NotNull Node<T> delete) {
if(!delete.hasLeftChild() && delete.hasRightChild()) return null;
//delete有两个Child
Node<T> successor = delete.getSuccessor();
delete.setData(successor.getData());
if (successor.hasRightChild())
//successor有右子节点
return deleteNodeWithOneChild(successor);
else
return deleteNodeWithoutChild(successor);
}
}
AVL类
package TreeStructure.BinaryTree;
import org.jetbrains.annotations.NotNull;
import static TreeStructure.BinaryTree.Node.unbalancedNode;
public class AVL<T extends Comparable<T>> extends BST<T> {
@Override
public Node<T> insert(T key) {
Node<T> insertNode = super.insert(key);
rotate(unbalancedNode(insertNode));
return insertNode;
}
@Override
public Node<T> delete(T key) {
Node<T> deleteNode = super.delete(key);
rotate(unbalancedNode(deleteNode));
return deleteNode;
}
private void rotate(Node<T> unbalancedNode) {
if (unbalancedNode == null) return;
if (Node.getHeight(unbalancedNode.getLeftChild()) > Node.getHeight(unbalancedNode.getRightChild())) {
if (Node.getHeight(unbalancedNode.getLeftChild().getLeftChild()) > Node.getHeight(unbalancedNode.getLeftChild().getRightChild()))
LL(unbalancedNode);
else LR(unbalancedNode);
} else {
if (Node.getHeight(unbalancedNode.getRightChild().getRightChild()) > Node.getHeight(unbalancedNode.getRightChild().getLeftChild()))
RR(unbalancedNode);
else RL(unbalancedNode);
}
}
private void LL(@NotNull Node<T> node) {
if (node == root) root = node.getLeftChild();
node.getLeftChild().setParent(node.getParent());
if (node.isLeftChildOf(node.getParent())) node.getParent().setLeftChild(node.getLeftChild());
if (node.isRightChildOf(node.getParent())) node.getParent().setRightChild(node.getLeftChild());
if (node.getLeftChild().getRightChild() != null) node.getLeftChild().getRightChild().setParent(node);
node.setParent(node.getLeftChild());
node.setLeftChild(node.getParent().getRightChild());
node.getParent().setRightChild(node);
}
private void RR(@NotNull Node<T> node) {
if (node == root) root = node.getRightChild();
node.getRightChild().setParent(node.getParent());
if (node.isRightChildOf(node.getParent())) node.getParent().setRightChild(node.getRightChild());
if (node.isLeftChildOf(node.getParent())) node.getParent().setLeftChild(node.getRightChild());
if (node.getRightChild().getLeftChild() != null) node.getRightChild().getLeftChild().setParent(node);
node.setParent(node.getRightChild());
node.setRightChild(node.getParent().getLeftChild());
node.getParent().setLeftChild(node);
}
private void LR(@NotNull Node<T> node) {
if (node == root) root = node.getLeftChild().getRightChild();
RR(node.getLeftChild());
LL(node);
}
private void RL(@NotNull Node<T> node) {
if (node == root) root = node.getRightChild().getLeftChild();
LL(node.getRightChild());
RR(node);
}
}