huangfox

冰冻三尺,非一日之寒!

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

一)AVL定义

计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G.M. Adelson-VelskyE.M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

出自维基百科:http://zh.wikipedia.org/wiki/AVL%E6%A0%91

 

二)AVL的实现(java)

package com.fox.avl;

import java.util.ArrayList;
import java.util.List;

public class AVLTree03<T extends Comparable<? super T>> {
	private static class AvlNode<T> {
		T e;
		AvlNode<T> l;
		AvlNode<T> r;
		int h;

		public AvlNode(T e, AvlNode<T> l, AvlNode<T> r) {
			super();
			this.e = e;
			this.l = l;
			this.r = r;
			this.h = 0;
		}

		public AvlNode(T e) {
			this(e, null, null);
		}

	}

	AvlNode<T> root = null;

	private int height(AvlNode<T> t) {
		return (t == null) ? -1 : t.h;
	}

	public void insert(T x) {
		root = insert(x, root);
	}

	private AvlNode<T> insert(T x, AvlNode<T> t) {
		if (t == null)
			return new AvlNode<T>(x);
		int cr = x.compareTo(t.e);
		if (cr < 0) {
			t.l = insert(x, t.l);
			if (height(t.l) - height(t.r) == 2)
				if (x.compareTo(t.l.e) < 0)
					t = rotateWithLeftChild(t);
				else
					t = doubleWithLeftChild(t);
		} else if (cr > 0) {
			t.r = insert(x, t.r);
			if (height(t.r) - height(t.l) == 2)
				if (x.compareTo(t.r.e) > 0)
					t = rotateWithRightChild(t);
				else
					t = doubleWithRightChild(t);
		} else
			;
		t.h = Math.max(height(t.l), height(t.r)) + 1;
		return t;
	}

	// RL
	private AvlNode<T> doubleWithRightChild(AvlNode<T> t) {
		t.r = rotateWithLeftChild(t.r);
		return rotateWithRightChild(t);
	}

	// RR
	private AvlNode<T> rotateWithRightChild(AvlNode<T> t) {
		AvlNode<T> t1 = t.r;
		t.r = t1.l;
		t1.l = t;
		t.h = Math.max(height(t.l), height(t.r)) + 1;
		t1.h = Math.max(height(t1.r), t.h) + 1;
		return t1;
	}

	// LR
	private AvlNode<T> doubleWithLeftChild(AvlNode<T> t) {
		t.l = rotateWithRightChild(t.l);
		return rotateWithLeftChild(t);
	}

	// LL
	private AvlNode<T> rotateWithLeftChild(AvlNode<T> t) {
		AvlNode<T> t1 = t.l;
		t.l = t1.r;
		t1.r = t;
		t.h = Math.max(height(t.l), height(t.r)) + 1;
		t1.h = Math.max(height(t1.l), t.h) + 1;
		return t1;
	}

	public boolean contain(T x) {
		return contain(x, root);
	}

	private boolean contain(T x, AvlNode<T> t) {
		if (t == null)
			return false;
		int cr = x.compareTo(t.e);
		if (cr < 0)
			return contain(x, t.l);
		else if (cr > 0)
			return contain(x, t.r);
		else
			return true;
	}

	public void display() {
		List<AvlNode<T>> ts = new ArrayList<AvlNode<T>>();
		ts.add(root);
		displayLevel(ts);
	}

	// 中序遍历
	private void display(AvlNode<T> t) {
		if (t == null)
			return;
		System.out.println(t.e + "[" + t.h + "]");
		display(t.l);
		display(t.r);
	}

	// 层次遍历
	private void displayLevel(List<AvlNode<T>> t) {
		if (t.size() == 0)
			return;
		List<AvlNode<T>> ts = new ArrayList<AvlNode<T>>();
		for (AvlNode<T> t_ : t) {
			if (t_ == null)
				continue;
			System.out.print(t_.e + "[" + t_.h + "]\t");
			if (t_.l != null)
				ts.add(t_.l);
			if (t_.r != null)
				ts.add(t_.r);
		}
		System.out.println();
		displayLevel(ts);
	}

	public static void main(String[] f) {
		AVLTree03<Integer> avl = new AVLTree03<Integer>();
		avl.insert(1);
		avl.insert(12);
		avl.insert(3);
		avl.insert(8);
		avl.insert(2);
		avl.insert(11);
		avl.display();
		System.out.println(avl.contain(1));
		System.out.println(avl.contain(10));
	}
}

这里注意插入节点的四种情况,下面一一细说。

1)左左情况

t1=t.l;

step1:t.l = t1.r ;

step2: t1.r = t ;

经过“旋转”后如下图:

2)右右情况

t1 = t.r ;

step1: t.r = t1.l ;

step2: t1.l = t ;

经过旋转后如下图:

3)左右情况

t1 = t.l ;

t2 = t.r ;

step1: t1.r = t2.l ;

step2: t2.l = t1 ;

以上步骤和右右情况一致,因此可以看成是对t1做右右操作,旋转后如下图:

这个又和左左一模一样,因此在对t节点做左左操作。从代码中可以清楚的看出:

        // LR
	private AvlNode<T> doubleWithLeftChild(AvlNode<T> t) {
		t.l = rotateWithRightChild(t.l);//先对t1做右右操作
		return rotateWithLeftChild(t);//在对t做左左操作
	}

4)右左情况

和3)类似,不再复述。

 

AVL的插入操作有点小复杂,但是删除操作比插入更复杂,如果偷懒那么还是选用标记删除吧!

 

 

posted on 2012-06-28 13:30  huangfox  阅读(1048)  评论(0编辑  收藏  举报