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

来源:互联网 发布:淘宝怎样开通全球购 编辑:程序博客网 时间:2024/06/05 19:45

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

(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失去平衡。                                                    LL型

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

(2)RR型

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

新插入的节点是插在结点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的左孩子。

                               LR型

第二次旋转: 调整不平衡因子。将支撑结点由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的右孩子。

RL型

第二次旋转: 调整不平衡因子。将支撑结点由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");elseprintTree(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);elseroot = 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;elsereturn 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;}}


1 0