1 概念
1.1 平衡
任何节点深度均不得过深
1.2 AVL树
每个节点的左子树和右子树高度最多差 1 的二叉查找树(空树高度定义为 -1)
带有平衡条件的二叉查找树
2 旋转
插入操作可能破坏平衡,需要通过旋转(rotation)进行修正。
设需要重新平衡的节点为 t,t 的两棵子树高度差应该为 2(平衡时高度差 <= 1)。
2.1 单旋转
对 t 的左儿子的左子树进行一次插入 / 对 t 的右儿子的右子树进行一次插入。
以对 t 的左儿子的左子树进行一次插入为例:
- 对 t 的左儿子的左子树进行插入 e,破坏了平衡
- 用 t 的左儿子 a 替换 t 作为新的根,t 作为 a 的右子树,原来 a 的右子树作为 t 的左子树
- 平衡性质恢复
2.2 双旋转
对 t 的左儿子的右子树进行一次插入 / 对 t 的右儿子的左子树进行一次插入。
双旋转等价于两次单旋转。
以对 t 的左儿子的右子树进行一次插入为例:
大写字母代表子树(E / F 可能为空),小写字母代表节点
- 对 t 的左儿子的右子树 d 进行插入,破坏了平衡
- 对 t 的左儿子的右子树进行单旋转
- 对 t 的左子树进行单旋转
- 平衡性质恢复
3 实现
AVL tree
package chapter4.avl_tree;
import chapter4.Element;
import chapter4.Node;
public class AVLTree {
private int size; //剩余节点数
private int deleteSize; //懒惰删除节点数
private Node root;
private Node min;
private Node max;
AVLTree() {
root = null;
max = null;
min = null;
}
public void clear() {
root = null;
}
public boolean isEmpty() {
return root == null;
}
public boolean contains(Element x) {
return contains(x, root);
}
public void insert(Element x) {
root = insert(x, root);
}
public void remove(Element x) {
remove(x, root);
}
public Element findMin() {
findMin(root);
return min.element;
}
public Element findMax() {
findMax(root);
return max.element;
}
public void printTree() {
printTree(root);
}
private boolean contains(Element x, Node t) {
if (t == null) {
return false;
}
int comp = x.compareTo(t.element);
if (comp > 0) { //大于节点值,右子树中搜索
return contains(x, t.right);
} else if (comp < 0) { //小于节点值,左子树中搜索
return contains(x, t.left);
} else {
return true;
}
}
private Node insert(Element x, Node t) {
if (t == null) {
return new Node(x);
}
int comp = x.compareTo(t.element);
if (comp > 0) { //大于节点值,右子树中插入
t.right = insert(x, t.right);
if (height(t.right) - height(t.left) == 2) {
if (x.compareTo(t.right.element) > 0) { //右儿子的右子树
t = rotateRight(t);
} else { //右儿子的左子树
t = doubleRight(t);
}
}
t.height = Math.max(height(t.left), height(t.right)) + 1;
size++;
} else if (comp < 0) { //小于节点值,左子树中插入
t.left = insert(x, t.left);
if (height(t.left) - height(t.right) == 2) {
if (x.compareTo(t.left.element) < 0) { //左儿子的左子树
t = rotateLeft(t);
} else { //左儿子的右子树
t = doubleLeft(t);
}
}
t.height = Math.max(height(t.left), height(t.right)) + 1;
size++;
} else {
t.count++;
}
return t;
}
private void remove(Element x, Node t) {
if (t == null) {
return;
}
int comp = x.compareTo(t.element);
if (comp > 0) { //大于节点值,右子树中搜索
remove(x, t.right);
} else if (comp < 0) { //小于节点值,左子树中搜索
remove(x, t.left);
} else { //找到要删除的节点
if (t.count == 0) { //节点已经被删除
return;
}
t.count--;
if (t.count == 0) { //懒惰删除后count为0,删除节点数增加1,剩余节点数减少1
deleteSize++;
size--;
}
if (deleteSize >= size) { //删除的节点数大于剩余节点数,进行彻底删除
delete(root);
}
}
}
private Node delete(Node t) {
if (t == null) {
return null;
}
if (t.count > 0) { //不需要删除
t.left = delete(t.left);
t.right = delete(t.right);
} else {
if (t.right == null) { //右子树为空,把左子树拼接上来(或左右子树都为空,直接设置为空)
t = t.left;
} else if (t.left == null) { //左子树为空,把右子树拼接上来
t = t.right;
} else { //左右子树都不为空,随机用右子树最小节点或左子树最大节点替换,以消除树的偏向
double random = Math.random();
if (random < 0.5) {
findMin(t.right);
if (min == null) { //右子树节点都已被懒惰删除,彻底删除右子树
t.right = null;
} else {
t.element = min.element;
t.count = min.count;
min.count = 0;
}
} else {
findMax(t.left);
if (max == null) {
t.left = null;
} else {
t.element = max.element;
t.count = max.count;
max.count = 0;
}
}
t = delete(t);
}
}
return t;
}
private void findMin(Node t) {
if (t == null)
return;
if (t.count > 0) { //节点未被删除
min = t;
}
findMin(t.left);
if (min == null) { //左子树节点均被删除
findMin(t.right);
}
}
private void findMax(Node t) {
if (t == null)
return;
if (t.count > 0) { //节点未被删除
max = t;
}
findMax(t.right);
if (max == null) { //左子树节点均被删除
findMax(t.left);
}
}
private int height(Node t) {
if (t == null) {
return -1;
} else {
return t.height;
}
}
/** * 左儿子单旋转 */
private Node rotateLeft(Node t) {
Node a = t.left;
t.left = a.right;
a.right = t;
t.height = Math.max(height(t.left), height(t.right)) + 1;
a.height = Math.max(height(a.left), height(a.right)) + 1;
return a;
}
/** * 右儿子单旋转 */
private Node rotateRight(Node t) {
Node a = t.right;
t.right = a.left;
a.left = t;
t.height = Math.max(height(t.left), height(t.right)) + 1;
a.height = Math.max(height(a.left), height(a.right)) + 1;
return a;
}
/** * 左儿子双旋转 */
private Node doubleLeft(Node t) {
t.left = rotateRight(t.left);
return rotateLeft(t);
}
/** * 右儿子双旋转 */
private Node doubleRight(Node t) {
t.right = rotateLeft(t.right);
return rotateRight(t);
}
/** * 后序遍历 * * @param t 父节点 */
private void printTree(Node t) {
if (t != null) {
printTree(t.left);
printTree(t.right);
if (t.count > 0) { //打印未被删除节点
t.element.print();
System.out.println(" height:" + t.height + " count:" + t.count);
}
}
}
}
节点
package chapter4;
public class Node implements Comparable<Node> {
public Node left;
public Node right;
public Element element;
public int count;
public int height; //节点子树高度
public Node(Element element) {
this(element, null, null);
}
public Node(Element element, Node l, Node r) {
this.element = element;
left = l;
right = r;
count = 1;
height = 0;
}
@Override
public int compareTo(Node node) {
return this.element.compareTo(node.element);
}
}
节点元素
package chapter4;
public class Element implements Comparable<Element> {
private int type; //数据类型
private int integer;
private String string;
public Element(int i) {
integer = i;
type = 1;
}
public Element(String s) {
string = s;
type = 2;
}
@Override
public int compareTo(Element o) {
if (type == 1) {
return Integer.compare(this.integer, o.integer);
} else if (type == 2) {
return Integer.compare(this.string.length(), o.string.length());
} else {
System.out.println("无法比较");
return 0;
}
}
public void print() {
if (type == 1) {
System.out.print(integer);
} else if (type == 2) {
System.out.print(string);
}
}
}
测试类
package chapter4.avl_tree;
import chapter4.Element;
public class Test {
public static void main(String args[]) {
AVLTree tree = new AVLTree();
Element element1 = new Element(3);
Element element2 = new Element(2);
Element element3 = new Element(1);
Element element4 = new Element(4);
Element element5 = new Element(5);
Element element6 = new Element(6);
Element element7 = new Element(7);
Element element8 = new Element(16);
Element element9 = new Element(15);
Element element10 = new Element(14);
Element element11 = new Element(13);
Element element12 = new Element(12);
Element element13 = new Element(11);
Element element14 = new Element(10);
Element element15 = new Element(8);
Element element16 = new Element(9);
tree.insert(element1);
tree.insert(element2);
tree.insert(element3);
tree.insert(element4);
tree.insert(element5);
tree.insert(element6);
tree.insert(element7);
tree.insert(element8);
tree.insert(element9);
tree.insert(element10);
tree.insert(element11);
tree.insert(element12);
tree.insert(element13);
tree.insert(element14);
tree.insert(element15);
tree.insert(element16);
System.out.println("intTree:");
tree.printTree();
System.out.println();
System.out.print("min:");
tree.findMin().print();
System.out.println();
System.out.print("max:");
tree.findMax().print();
System.out.println();
System.out.println("contains 14:" + tree.contains(element10));
System.out.println();
tree.remove(element11);
tree.insert(element2);
System.out.println("intTree:");
tree.printTree();
System.out.println();
tree.clear();
System.out.println("isEmpty:" + tree.isEmpty());
}
}
输出
intTree:
1 height:0 count:1
3 height:0 count:1
2 height:1 count:1
5 height:0 count:1
6 height:1 count:1
4 height:2 count:1
8 height:0 count:1
10 height:0 count:1
9 height:1 count:1
12 height:0 count:1
11 height:2 count:1
14 height:0 count:1
16 height:0 count:1
15 height:1 count:1
13 height:3 count:1
7 height:4 count:1
min:1
max:16
contains 14:true
intTree:
1 height:0 count:1
3 height:0 count:1
2 height:1 count:2
5 height:0 count:1
6 height:1 count:1
4 height:2 count:1
8 height:0 count:1
10 height:0 count:1
9 height:1 count:1
12 height:0 count:1
11 height:2 count:1
14 height:0 count:1
16 height:0 count:1
15 height:1 count:1
7 height:4 count:1
isEmpty:true