BST和AVL复习。
二叉排序树的3个主要性质:
对于插入操作,按照上述规则,递归即可。
但是对于删除操作,涉及到排序规则的保持,可分3中情况考虑:
严蔚敏版的数据结构,对于第3中情况,有不同两种处理方式:
其中第2种方式与上面第3中情况的处理方式类似,容易理解,因为要保持顺序性,要么直接前驱、要么直接后继来代替,才能保持有序; 第1种方式稍微有点绕,不过删除节点右子树成为直接前驱的右子树,也是保持着有序性的。
不过从代码实现的简洁程度和便于理解的角度,我倾向于使用上面第3种情况的处理方式。
BST在插入关键字有序时,会蜕化为单支树,所以引出了平衡二叉树。平衡二叉树除了具有二叉搜索树的性质外,还有两个性质:
节点高度的定义为从该节点到叶子节点的路径(边)的长度,叶子节点的高度为0。
从多出来的两个性质可以看出,变化主要在插入和删除操作上,对于这两个操作,除了要保持顺序性,现在还要保持平衡。
假设在节点v上出现了不平衡:
bf = height(v.left) - height(v.right)
bf2 = height(v.left.left) - height(v.left.right)
bf3 = height(v.right.left) - height(v.right.right)
则有四种不平衡的场景:
场景梳理出来,就有对应的解决方法:
另一种方式是使用平衡因子,也就是上面算的bf来作为节点属性进行平衡操作,具体参见严蔚敏版的数据结果。 不过,同样,从代码实现和便于理解的角度来看,我倾向于使用高度来作为节点属性。
BST和AVL原理简单清晰,但是真要自己动手实现起来,还是很麻烦的,尤其是想把代码写的简洁。 惯例吐槽,严蔚敏版的数据结构,读起来确实累。
附录:
代码参考了VISUALGO的实现,没有注释,结合上面的描述对应着看吧。
/**
* avl tree
* @author lhz
*/
public class AvlTree extends BinarySearchTree {
public void insert(Comparable v) {
if (v == null) {
throw new NullPointerException();
}
root = insert(root, v);
}
private int height(Node v) {
return v == null ? -1 : v.height;
}
private Node rotateRight(Node v) {
Node lc = v.left;
v.left = lc.right;
if (lc.right != null) {
lc.right.parent = v;
}
lc.right = v;
lc.parent = v.parent;
v.parent = lc;
v.height = Math.max(height(v.left), height(v.right)) + 1;
lc.height = Math.max(height(lc.left), height(lc.right)) + 1;
return lc;
}
private Node rotateLeft(Node v) {
Node rc = v.right;
v.right = rc.left;
if (rc.left != null) {
rc.left.parent = v;
}
rc.parent = v.parent;
rc.left = v;
v.parent = rc;
v.height = Math.max(height(v.left), height(v.right)) + 1;
rc.height = Math.max(height(rc.left), height(rc.right)) + 1;
return rc;
}
@Override
protected Node insert(Node v, Comparable k) {
v = super.insert(v, k);
v = balance(v);
return v;
}
public void remove(Comparable v) {
root = remove(root, v);
}
@Override
protected Node remove(Node v, Comparable k) {
v = super.remove(v, k);
v = balance(v);
return v;
}
protected Node balance(Node k) {
if (k == null) {
return null;
}
int balance = height(k.left) - height(k.right);
if (balance == 2) {
int balance2 = height(k.left.left) - height(k.left.right);
if (balance2 == 1) {
k = rotateRight(k);
} else {
k.left = rotateLeft(k.left);
k = rotateRight(k);
}
} else if (balance == - 2) {
int balance2 = height(k.right.left) - height(k.right.right);
if (balance2 == -1) {
k = rotateLeft(k);
} else {
k.right = rotateRight(k.right);
k = rotateLeft(k);
}
}
k.height = Math.max(height(k.left), height(k.right)) + 1;
return k;
}
}
/**
* avl tree
* @author lhz
*/
public class BinarySearchTree {
class Node {
public Comparable value;
public int bf; //balance factor
public int height;
public int ref;//same value reference count
public Node left;
public Node right;
public Node parent;
public Node(Comparable v) {
value = v;
}
public String toString() {
return String.format("[%s:%d, h=%d, bf=%d]", value, ref, height, bf);
}
}
protected Node root;
public Comparable min(Node v) {
if (v == null) {
return null;
}
while (v.left != null) {
v = v.left;
}
return v.value;
}
public Comparable max(Node v) {
if (v == null) {
return null;
}
while (v.right != null) {
v = v.right;
}
return v.value;
}
public Node search(Node v, Comparable k) {
if (v == null) {
return null;
}
int compared = k.compareTo(v.value);
if (compared > 0) {
return search(v.right, k);
} else if (compared < 0) {
return search(v.left, k);
} else {
return v;
}
}
protected Node insert(Node v, Comparable k) {
if (v == null) {
return new Node(k);
}
int compared = k.compareTo(v.value);
if (compared > 0) {
v.right = insert(v.right, k);
v.right.parent = v;
} else if (compared < 0) {
v.left = insert(v.left, k);
v.left.parent = v;
} else {
v.ref++;
}
return v;
}
protected Node remove(Node v, Comparable k) {
if (v == null) {
return null;
}
int compared = k.compareTo(v.value);
if (compared > 0) {
v.right = remove(v.right, k);
if (v.right != null) v.right.parent = v;
} else if (compared < 0) {
v.left = remove(v.left, k);
if (v.left != null) v.left.parent = v;
} else {
if (v.right == null && v.left == null) {
return null;
} else if (v.left != null && v.right != null) {
Comparable s = successor(v);
v.value = s;
v.right = remove(v.right, s);
} else if (v.left != null) {
return v.left;
} else if (v.right != null) {
return v.right;
}
}
return v;
}
protected Comparable successor(Node v) {
if (v.right != null) {
return min(v.right);
} else {
Node p = v.parent;
Node c = v;
while (p != null && p.right == c) {
c = p;
p = p.parent;
}
return p == null ? null : p.value;
}
}
protected Comparable predecessor(Node v) {
if (v.left != null) {
return max(v.left);
} else {
Node p = v.parent;
Node c = v;
while (p != null && p.left == c) {
c = p;
p = p.parent;
}
return p == null ? null : p.value;
}
}
}