您的位置:首页 > 其它

平衡二叉树(Balance Binary Tree) --AVL树

2016-02-18 12:08 393 查看
上一节介绍的二叉查找树的查找效率取决于二叉排序树的形态,而构造一棵形态均匀的二叉查找树与节点插入的次序有关,而插入的顺序往往是不可预测的。

(1)若将最大值或最小值首先插入,整个二叉查找树将没有左子树或右子树。

(2)在最坏情况下,插入的次序已经是有序的,此时二叉查找树将成为一棵斜树,此时就成了一个线性边,查找的效率骤然下降,时间复杂度边为O(n)

(3)所以二叉查找树的效率介于 O(logN)和O(n)之间。

基于上面的情况,这就需要我们找到一种动态平衡的方法,对于任意的插入序列都能构造一棵形态均匀、平衡的二叉查找树,这就是我们下面介绍的平衡二叉树

平衡二叉树性质:

(1)根节点的左子树和右子树的深度最多相差1。 (2)根节点的左子树和右子树叶都是一棵平衡二叉树。

平衡因子: 每个结点的平衡因子是该结点的左子树与右子树的深度之差。

构造平衡二叉树的思想: 在构造二叉查找树的过程中,每当插入一个结点,首先检查插入后是否破坏了树的平衡性,若破坏对树进行调整,使插入后成为平衡二叉树。


下面,介绍一下平衡二叉树插入时就对树进行调整的4种情况:

(1)LL型

BL 、 BR 分别表示结点B
的左右子树, AR 为结点A的右子树,且深度都是h。 将节点X 插到B 的左子树上,导致节点A 的平衡因子有1变为了2,A失去平衡。


新插入的节点是插在结点A的左孩子的左子树上,属于LL型。根据扁担原理,将支撑结点由A 改为B,并进行顺时针旋转,旋转后,A和BR
发生冲突,采用旋转优先原则,将A结点作为B结点的右孩子,B结点的右孩子脱落下来成为A结点的左孩子。


(2)RR型

BL
BR
分别表示结点B 的左右子树, AR
为结点A的右子树,且深度都是h。 将节点X 插到B 的右子树上,导致节点A 的平衡因子有-1变为了-2,A失去平衡。


新插入的节点是插在结点A的右孩子的右子树上,属于LL型。根据扁担原理,将支持节点右A 改为B,并进行逆时针旋转,旋转后,A和BL
发生冲突,采用旋转优先原则,将A结点作为B结点的左孩子,B结点的左孩子脱落下来成为A结点的右孩子。


(3)LR型

节点X插在根结点的左孩子的右子树上,使结点A的平衡因子有1变为了2,A结点失去平衡,这里属于LR型,需要旋转2次。

第一次旋转: 根结点不动,先调整结点A的左子树。将支撑节点由B调整为C,进行逆时针,此时B和C的左子树发生冲突,旋转优先,将B作为C的左孩子,C的左孩子脱落下来成为B的右孩子,此时C成为了A的左孩子。





第二次旋转: 调整不平衡因子。将支撑结点由A调整为C,相应的进行顺时针旋转。此时,A和C的右孩子冲突,将A作为C的右孩子,C的右孩子脱落下来成为A的左孩子。

(4)RL型

和LR型类似,也需要两次调整。节点X插在根结点的右孩子的左子树上,使结点A的平衡因子有-1变为了-2,A结点失去平衡,这里属于RL型,需要旋转2次

第一次旋转: 根结点不动,先调整结点A的右子树。将支撑节点由B调整为C,进行顺时针,

此时B和C的右子树发生冲突,旋转优先,将B作为C的右孩子,C的右孩子。





第二次旋转: 调整不平衡因子。将支撑结点由A调整为C,相应的进行逆时针旋转。

此时,A和C的左孩子冲突,将A作为C的左孩子,C的左孩子脱落下来成为A的右孩子。



通过上面的分析,我们已经明白了构建平衡二叉树的及基本过程及思路,这也是平衡二叉树的精华所在,无论插入的序列是怎么样,我们都能通过调整构建一棵平衡二叉树,保证二叉树中的每个节点的平衡因子都不会大于1,保证了树的深度达到最浅,从而比较的次数就会更少,时间复杂度就会降低,在平衡二叉树中查找的时间复杂度为O(logN)。比二叉查找树性能更加好。



一个平衡二叉树的简单实现:

package org.TT.BalanceTree;

/**
*  平衡二叉树简单实现  
*/
public class AvlTree<T extends Comparable<? super T>> {

private AvlNode<T> root;// AVL树根

/**
* 初始化为空树
*/
public AvlTree() {
root = null;
}

/**
* 在AVL树中插入数据
*/
public void insert(T x) {
root = insert(x, root);
}

/**
* 在AVL树中找最小的数据
*/
public T findMin() {
if (isEmpty())
System.out.println("树空");
return findMin(root).element;
}

/**
* 在AVL树中找最大的数据
*/
public T findMax() {
if (isEmpty())
System.out.println("树空");
return findMax(root).element;
}

/**
* 搜索,查找记录,找到返回 true 否则返回false
*/
public boolean find(T x) {
return find(x, root);
}

/**
* 清空AVL 树
*/
public void clear() {
root = null;
}

/**
* 判断是否为空
*/
public boolean isEmpty() {
return root == null;
}

/**
* 排序输出AVL树, 采用中序输出
*/
public void printTree() {
if (isEmpty())
System.out.println("Empty tree");
else
printTree(root);
}

/**
* 以根节点开始,将新节点插入到合适的位置
*/
private AvlNode<T> insert(T x, AvlNode<T> root) {
if (root == null)
return new AvlNode<T>(x, null, null);

int compareResult = x.compareTo(root.element);

if (compareResult < 0) {
root.left = insert(x, root.left);// 将x插入左子树中
if (height(root.left) - height(root.right) == 2)// 打破平衡
if (x.compareTo(root.left.element) < 0)// LL型(左左型)
root = rotateWithLeftChild(root);
else
root = doubleWithLeftChild(root);	// LR型(左右型)
} else if (compareResult > 0) {
root.right = insert(x, root.right);// 将x插入右子树中
if (height(root.right) - height(root.left) == 2)// 打破平衡
if (x.compareTo(root.right.element) > 0)// RR型(右右型)
root = rotateWithRightChild(root);
else
// RL型
root = doubleWithRightChild(root);
} else
; // 重复数据,什么也不做
root.height = Math.max(height(root.left), height(root.right)) + 1;// 更新高度
return root;
}

/**
* 找最小值
*/
private AvlNode<T> findMin(AvlNode<T> n) {
if (n == null)
return n;
while (n.left != null)
n = n.left;
return n;
}

/**
* 找最大值
*/
private AvlNode<T> findMax(AvlNode<T> n) {
if (n == null)
return n;
while (n.right != null)
n = n.right;
return n;
}

/**
* 搜索(查找),以某个节点作为根开始查找
*/
@SuppressWarnings("unchecked")
private boolean find(T x, AvlNode<?> root) {
while (root != null) {
int compareResult = x.compareTo((T) root.element);

if (compareResult < 0)
root = root.left;
else if (compareResult > 0)
root = root.right;
else
return true; //找到
}
return false; //未找到
}

/**
* 中序遍历AVL树
*/
private void printTree(AvlNode<T> n) {
if (n != null) {
printTree(n.left);
System.out.print(n.element + "  ");
printTree(n.right);
}
}

/**
* 求AVL树的高度
*/
private int height(AvlNode<T> n) {
return n == null ? -1 : n.height;
}

/**
* 带左子树旋转,适用于LL型
*/
private AvlNode<T> rotateWithLeftChild(AvlNode<T> n) {
AvlNode<T> k = n.left;
n.left = k.right;
k.right = n;
n.height = Math.max(height(n.left), height(n.right)) + 1;
k.height = Math.max(height(k.left), n.height) + 1;
return k;
}

/**
* 带右子树旋转,适用于RR型
*/
private AvlNode<T> rotateWithRightChild(AvlNode<T> n) {
AvlNode<T> k = n.right;
n.right = k.left;
k.left = n;
n.height = Math.max(height(n.left), height(n.right)) + 1;
k.height = Math.max(height(k.right), n.height) + 1;
return k;
}

/**
* 双旋转,适用于LR型
*/
private AvlNode<T> doubleWithLeftChild(AvlNode<T> n) {
n.left = rotateWithRightChild(n.left);
return rotateWithLeftChild(n);
}

/**
* 双旋转,适用于RL型
*/
private AvlNode<T> doubleWithRightChild(AvlNode<T> n) {
n.right = rotateWithLeftChild(n.right);
return rotateWithRightChild(n);
}

public static void main(String[] args) {
AvlTree<Integer> t = new AvlTree<Integer>();

int[] value = { 6, 1, 20, 7, 10, 8, 9, 3, 14, 5, 18};
for (int v : value) {
t.insert(v);
}
System.out.println("中序遍历: ");
t.printTree();

System.out.println();
System.out.println("树的高度: " + t.height(t.root));

System.out.println("最小值: " + t.findMin());
System.out.println("最大值: " + t.findMax());
System.out.println("查找“10”: " + t.find(10));
}

}

结果:



AVL节点类:

package org.TT.BalanceTree;

public class AvlNode<T> {

T element; // 节点中的数据
AvlNode<T> left; // 左孩子
AvlNode<T> right; // 右孩子
int height; // 节点的高度

AvlNode(T theElement) {
this(theElement, null, null);
}

AvlNode(T theElement, AvlNode<T> lt, AvlNode<T> rt) {
element = theElement;
left = lt;
right = rt;
height = 0;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: