一、談二叉搜索樹
增加的最壞時間複雜度 | 刪除的最壞時間複雜度 | 查詢的最壞時間複雜度 | |
鏈表 | O(N) + O(1) | O(N) + O(1) | O(N) |
---|---|---|---|
二叉搜索樹 | O(N) + O(1) | O(N) + O(1) | O(N) |
對於二叉搜索樹,如果添加數據的時候數據隨機分佈的話,插入結點的時間複雜度會是O(h)、O(logN)。但是考慮下面兩種極端情況,當**數據從小到大或者從大到小插入**的時候,裂開,複雜度直接變爲O(N),而且**二叉搜索樹變得和普通鏈表**一樣了。
不但插入結點會涼涼,刪除結點可能也會變成鏈表,考慮下面情況:
其實出現上述情況的根源還是二叉搜索樹失衡了。
二、AVL樹
1、AVL樹(自平衡)特點
每個結點的平衡因子的絕對值小於等於1,一旦超過就是失衡,balanceFactor計算方法:平衡因子 = 左子樹高度 - 右子樹高度
。
AVL樹增加刪除查詢結點的時間複雜度都是O(h)、O(logN)。
2、設計結構圖:
3、注意點
寫代碼要搞清楚以下兩個點:
① 什麼時候會出現失衡?
二叉搜索樹插入結點和刪除結點的時候會出現失衡情況。其實插入結點和刪除結點也是不一樣的,插入結點造成的失衡,只要往上找到第一個失衡的祖父結點,之後旋轉同時將該結點及子結點的高度更新後,祖父結點之上失衡的結點也會變得平衡。然後刪除結點恰恰相反,可能牽一髮而動全身,不過最壞情況也不過是執行O(logN)次旋轉操作。
② 怎麼解決失衡?四種旋轉情況對號入座
4、四種旋轉情況(g、p、n三個結點)分析
第一種情況:LL造成的失衡,g結點右旋轉。
第二種情況:RR造成的失衡,g結點左旋轉。
第三種情況:LR造成的失衡,先將p結點左旋轉,再將g結點右旋轉。
第四種情況:RL造成的失衡,先將p結點右旋轉,再將g結點左旋轉。
旋轉情況比較簡單,主要還是確定三個點,這三個點都是結點左右子樹中比較長的結點。其中三個點用來判斷是哪一種旋轉情況,而 g 和 p 結點是專門用來旋轉的。
模板設計模式搭好AVL樹插入結點和刪除結點的算法框架,在之前BST插入代碼裏面寫好接口,AVL樹實現相應解決失衡的方法。
5、代碼實現
public class BinarySearchTree<E> extends BinaryTree<E> {
private Comparator<E> comparator;
public BinarySearchTree(Comparator<E> comparator) {
this.comparator = comparator;
}
public BinarySearchTree() {
this(null); //調用有參構造
}
private int compare(E e1, E e2) {
if (comparator != null)
return comparator.compare(e1, e2);
else
return ((Comparable) e1).compareTo(e2);
}
public boolean contains(E element) {
return getNode(element) != null;
}
public Node<E> getNode(E element) {
Node<E> node = root;
while (node != null) {
int cmp = compare(element, node.element);
if (cmp > 0) {
node = node.right;
} else if (cmp < 0) {
node = node.left;
} else {
return node;
}
}
return null;
}
protected Node<E> createNode(Node<E> parent, E element) {
return new Node(parent, element);
}
/**
* BST插入一個結點
*
* @param element
*/
public void add(E element) {
Node<E> node = root;
if (root == null) { //插入第一個結點
root = createNode(null, element);
size++;
afterAdd(root); //判斷祖父結點們是否失衡在子類裏進行處理
return;
}
int cmp = 0;
Node<E> t = null;
while (node != null) {
t = node;
cmp = compare(element, node.element);
if (cmp > 0) { //待插入的元素比根結點元素大
node = node.right;
} else if (cmp < 0) { //待插入的元素比根結點元素小
node = node.left;
} else {
return;
}
}
if (cmp > 0) {
t.right = createNode(t, element);
afterAdd(t.right); //判斷祖父結點們是否失衡在子類裏進行處理
} else {
t.left = createNode(t, element);
afterAdd(t.left); //判斷祖父結點們是否失衡在子類裏進行處理
}
size++;
}
/**
* 模板設計模式 交給子類來實現
*
* @param node
*/
protected void afterAdd(Node<E> node) {}
/**
* BST刪除節點要分情況, 度爲0、1、2
*
* @param element
*/
public void remove(E element) {
Node<E> node = getNode(element);
if (node == null)
return;
if (getDegree(node) == 0) { //度爲0, 直接刪除即可
if (node.parent == null) //只有一個根結點
root = null;
if (node == node.parent.left)
node.parent.left = null;
else
node.parent.right = null;
afterRemove(node); //判斷祖父結點們是否失衡在子類裏進行處理
} else if (getDegree(node) == 1) { //度爲1, 找自己的左兒子或右兒子替代
if (node.left != null) { //找左兒子代替
if (node.parent == null) //只有一個根結點
root = node.left;
if (node.parent.left == node) {
node.parent.left = node.left;
node.left.parent = node.parent; //改變父結點
} else {
node.parent.right = node.left;
node.left.parent = node.parent; //改變父結點
}
afterRemove(node); //判斷祖父結點們是否失衡在子類裏進行處理
} else { //找右兒子代替
if (node.parent == null) //只有一個根結點
root = node.right;
if (node.parent.left == node) {
node.parent.left = node.right;
node.right.parent = node.parent; //改變父結點
} else {
node.parent.right = node.right;
node.right.parent = node.parent; //改變父結點
}
afterRemove(node); //判斷祖父結點們是否失衡在子類裏進行處理
}
} else { //度爲2, 找前驅或者後繼, 拷貝數據然後刪除找的結點
Node<E> t = getSuccessor(node);
node.element = t.element;
int s = getDegree(t); //前驅結點和後繼結點只可能是度爲1或者度爲0
if (s == 1) { //度爲1
if (t.left != null) {
if (t.parent.left == t) {
t.parent.left = t.left;
t.left.parent = t.parent;
} else {
t.parent.right = t.left;
t.left.parent = t.parent;
}
afterRemove(t); //判斷祖父結點們是否失衡在子類裏進行處理
} else {
if (t.parent.left == t) {
t.parent.left = t.right;
t.right.parent = t.parent;
} else {
t.parent.right = t.right;
t.right.parent = t.parent;
}
afterRemove(t); //判斷祖父結點們是否失衡在子類裏進行處理
}
} else { //度爲0
if (t.parent.left == t)
t.parent.left = null;
else
t.parent.right = null;
afterRemove(t); //判斷祖父結點們是否失衡在子類裏進行處理
}
}
}
/**
* 模板設計模式 交給子類來實現
*
* @param node
*/
protected void afterRemove(Node<E> node) {}
}
public class AVLTree<E> extends BinarySearchTree<E> {
public AVLTree(Comparator<E> comparator) {
super(comparator);
}
public AVLTree() {
super();
}
private class AVLNode<E> extends Node<E> {
private int height = 1;
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public AVLNode(Node parent, E element) {
super(parent, element);
}
/**
* 計算平衡因子
*
* @return
*/
public int balanceFactor() {
int leftHeight = this.left == null ? 0 : ((AVLNode<E>) this.left).height;
int rightHeight = this.right == null ? 0 : ((AVLNode<E>) this.right).height;
return leftHeight - rightHeight;
}
/**
* 更新高度
*/
public void updateHeight() {
int leftHeight = this.left == null ? 0 : ((AVLNode<E>) this.left).height;
int rightHeight = this.right == null ? 0 : ((AVLNode<E>) this.right).height;
height = 1 + Math.max(leftHeight, rightHeight);
}
/**
* 找到自己的左右子樹中長的那個
*
* @return
*/
public Node getTallerChild() {
int leftHeight = this.left == null ? 0 : ((AVLNode<E>) this.left).height;
int rightHeight = this.right == null ? 0 : ((AVLNode<E>) this.right).height;
if (leftHeight > rightHeight)
return left;
else
return right;
// TODO 相等情況
}
}
private boolean isBalance(Node<E> node) {
return Math.abs(((AVLNode<E>) node).balanceFactor()) <= 1;
}
private void updateHeight(Node<E> node) {
((AVLNode<E>) node).updateHeight();
}
@Override
protected Node<E> createNode(Node<E> parent, E element) {
return new AVLNode(parent, element);
}
/**
* 左旋轉
*
* @param g
*/
private void rotateLeft(Node<E> g) { //只需要動g 和 p
Node<E> p = g.right;
g.right = p.left;
p.left = g;
//p變爲根結點
p.parent = g.parent;
if (g.parent == null) { //旋轉的是根結點
root = p;
} else if (g.parent.left == g)
g.parent.left = p;
else if (g.parent.right == g)
g.parent.right = p;
//改變其他結點的parent
if (g.right != null)
g.right.parent = g;
g.parent = p;
//更新高度
updateHeight(g);
updateHeight(p);
}
/**
* 右旋轉
*
* @param g
*/
private void rotateRight(Node<E> g) { //只需要動g 和 p
Node<E> p = g.left;
g.left = p.right;
p.right = g;
//p變爲根結點
p.parent = g.parent;
if (g.parent == null) { //旋轉的是根結點
root = p;
} else if (g.parent.left == g)
g.parent.left = p;
else if (g.parent.right == g)
g.parent.right = p;
//改變其他結點的parent
if (g.left != null)
g.left.parent = g;
g.parent = p;
//更新高度
updateHeight(g);
updateHeight(p);
}
@Override
protected void afterAdd(Node<E> n) {
Node<E> grandparent = n;
while ((grandparent = grandparent.parent) != null) {
if (isBalance(grandparent)) { //結點平衡直接更新高度
updateHeight(grandparent);
} else { //結點不平衡需要旋轉操作
//先找到g p n 三個結點(g是第一個不平衡結點中比較長的孩子結點)
Node<E> parent = ((AVLNode<E>) grandparent).getTallerChild();
Node<E> node = ((AVLNode<E>) parent).getTallerChild();
if (grandparent.left == parent) {
if (parent.left == node) { //LL
rotateRight(grandparent);
} else if (parent.right == node) { //LR
rotateLeft(parent);
rotateRight(grandparent);
}
} else if (grandparent.right == parent) {
if (parent.left == node) { //RL
rotateRight(parent);
rotateLeft(grandparent);
} else if (parent.right == node) { //RR
rotateLeft(grandparent);
}
}
break; //添加結點引起的失衡只需旋轉一次
}
}
}
@Override
protected void afterRemove(Node<E> n) {
Node<E> grandparent = n;
while ((grandparent = grandparent.parent) != null) {
if (isBalance(grandparent)) { //結點平衡直接更新高度
updateHeight(grandparent);
} else { //結點不平衡需要旋轉操作
//先找到g p n 三個結點(g是第一個不平衡結點中比較長的孩子結點)
Node<E> parent = ((AVLNode<E>) grandparent).getTallerChild();
Node<E> node = ((AVLNode<E>) parent).getTallerChild();
if (grandparent.left == parent) {
if (parent.left == node) { //LL
rotateRight(grandparent);
} else if (parent.right == node) { //LR
rotateLeft(parent);
rotateRight(grandparent);
}
} else if (grandparent.right == parent) {
if (parent.left == node) { //RL
rotateRight(parent);
rotateLeft(grandparent);
} else if (parent.right == node) { //RR
rotateLeft(grandparent);
}
}
//刪除結點引起的失衡可能不止一次不要直接break
}
}
}
}