高度平衡树——AVL树的扩展

高度平衡树(height-balanced tree, hb tree)就是以高度作为平衡条件的树,其实就是AVL树的扩展。我们熟知的AVL树(hb[1] 树)的平衡条件是左右子树高度差不超过1,其实高度差可以是任意正整数。Hb[k]树就是左右子树高度差不超过k的树。

Hb[k]树的插入和删除算法基本上和AVL树是一样的。

在插入一个节点后可能会造成不平衡,只要对最小不平衡树做调整即可。下面就左子树大于右子树的情况进行分析。

如下图,情况可以分成A、B两种:

《高度平衡树——AVL树的扩展》

1) 对于情况A可以通过单旋使树达到平衡:

《高度平衡树——AVL树的扩展》

2) 对于情况B可以通过双旋使树达到平衡:

《高度平衡树——AVL树的扩展》

删除的算法和插入类似,都是对最小不平衡树进行调整。只是调整后树的高度可能会减1,上一级可能成为新的最小不平衡树,就要继续调整,最坏的情况下会一直调整到根节点。对于不平衡树的调整和插入时的差不多,只是比插入多了一种情况C。

对于情况C只需单旋就可以了,而且树的高度没有变化,就不用向上调整了。

《高度平衡树——AVL树的扩展》

其实C的情况可以和A合并为一种,这样插入和删除的平衡算法就可以统一了。

另外对于某些情况其实单旋和双旋都可以用:

如A的某些情况A’可以进行双旋

《高度平衡树——AVL树的扩展》

B的某些情况B’也可以进行单旋

《高度平衡树——AVL树的扩展》

上面两种情况都只会出现在k>=2时,他们的旋转都没有改变树的高度,删除的时候使用比较好。当然插入时也可以使用,但可能就会造成向上递归了,一般不提倡这样做。

经过测试发现hb[2]树的高度和AVL树(hb[1]树)差不多,但hb[2]树旋转的次数却不到AVL树的一半。而Hb[2]树的高度和红黑树也差不多,但旋转的次数只是比红黑树的一半多一点。个人认为hb[2]树的性能会比红黑树好,而且实现起来和AVL树一样简单。

现在再回头看上面A、B、C情况,发现最右边的子树高度n加上一个a值(0<=a<=k-1)后,树的平衡性仍然能够保持。也就是说,并不一定要在高度差为k+1时才去调整,在其他情况下也可以作调整,调整后的树它的左右子树高度差不超过k-a。

《高度平衡树——AVL树的扩展》

《高度平衡树——AVL树的扩展》

上面的结论有着总要的意义,我们可以得到一个更佳的调整策略。因为树的旋转主要发生在矮树上,因此我们可以让矮树“宽松”些,让高树“严谨”些。例如对于hb[2]树,我们可以这样改变:对于高度小于等于7的树,它的子树高度差不超过2,对于高度大于7的树,它的子树高度差不超过1,我们把这样的树称为hb[2,7,1]树。这样hb[2,7,1]树的平衡性可以和hb[1]树相当,性能和hb[2]树相当。个人认为把矮树的高度定义为6或7为最佳。

总的来看hb树其实只是对AVL树一个小小的扩展,牺牲一点的平衡性换来性能上大大的提升。我认为hb[2,7,1]树是其中最佳的选择,而且觉得它在各个方面都有取代红黑树的实力。

 

package tree;

public class HbTree {
	static class Node {
		static final Node nullNode = new Node();
		int key;
		Node left;
		Node right;
		int height;

		public Node() {
		}

		public Node(int key) {
			this.key = key;
			this.height = 1;
			left = nullNode;
			right = nullNode;
		}

		public void refreshheight() {
			height = Math.max(left.height, right.height) + 1;
		}

		public void print(int k) {
			String keyStr;
			if (key < 10) {
				keyStr = "  " + key;
			} else if (key < 100) {
				keyStr = " " + key;
			} else {
				keyStr = key + "";
			}
			System.out.print(keyStr + " ");
			if (right != null && right != nullNode) {
				right.print(k + 1);
			}
			if (left != null && left != nullNode) {
				System.out.println();
				for (int i = 0; i < k; i++) {
					System.out.print("    ");
				}
				left.print(k + 1);
			}
		}
	}

	private int k = 1;
	private Node root = Node.nullNode;

	public HbTree(int k) {
		this.k = k < 1 ? 1 : k;
	}

	private Node upLeft(Node root) {
		Node newRoot = root.left;
		root.left = newRoot.right;
		newRoot.right = root;
		root.refreshheight();
		newRoot.refreshheight();
		return newRoot;
	}

	private Node upRight(Node root) {
		Node newRoot = root.right;
		root.right = newRoot.left;
		newRoot.left = root;
		root.refreshheight();
		newRoot.refreshheight();
		return newRoot;
	}

	private Node balance(Node root) {
		int k = this.k;
		if (k > 1 && root.height > 7) {
			k--;
		}
		if (root.left.height - root.right.height > k) {
			if (root.left.left.height >= root.left.right.height) {
				root = upLeft(root);
			} else {
				root.left = upRight(root.left);
				root = upLeft(root);
			}
		} else if (root.right.height - root.left.height > k) {
			if (root.right.right.height >= root.right.left.height) {
				root = upRight(root);
			} else {
				root.right = upLeft(root.right);
				root = upRight(root);
			}
		} else {
			root.refreshheight();
		}
		return root;
	}

	private Node insert(Node root, int key) {
		if (root == Node.nullNode || root == null) {
			return new Node(key);
		} else if (root.key > key) {
			Node newNode = insert(root.left, key);
			if (newNode == null) {
				return null;
			} else {
				root.left = newNode;
			}
		} else if (root.key < key) {
			Node newNode = insert(root.right, key);
			if (newNode == null) {
				return null;
			} else {
				root.right = newNode;
			}
		} else {
			return null;
		}
		root.refreshheight();
		return balance(root);
	}

	private Node topMin(Node root) {
		if (root.left == Node.nullNode) {
			return root;
		} else {
			Node min = topMin(root.left);
			root.left = min.right;
			min.right = balance(root);
			return min;
		}
	}

	private Node remove(Node root, int key) {
		if (root == Node.nullNode) {
			return null;
		} else if (root.key == key) {
			if (root.right == Node.nullNode) {
				return root.left;
			} else {
				Node min = topMin(root.right);
				min.left = root.left;
				return balance(min);
			}
		} else if (root.key > key) {
			Node newLeft = remove(root.left, key);
			if (newLeft == null) {
				return null;
			} else {
				root.left = newLeft;
				return balance(root);
			}
		} else {
			Node newRight = remove(root.right, key);
			if (newRight == null) {
				return null;
			} else {
				root.right = newRight;
				return balance(root);
			}
		}
	}

	public void print() {
		if (root == Node.nullNode) {
			System.out.println("empty tree");
			return;
		}
		root.print(1);
		System.out.println();
	}

	public void insert(int key) {
		Node newNode = insert(root, key);
		if (newNode != null) {
			root = newNode;
		}
	}

	public void remove(int key) {
		Node newRoot = remove(root, key);
		if (newRoot != null) {
			root = newRoot;
		}
	}

	public int height() {
		return root.height;
	}
}

    原文作者:红黑树
    原文地址: https://my.oschina.net/linlifeng/blog/78398
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞