【数据结构】AVL树

来源:互联网 发布:js数组地址指向 编辑:程序博客网 时间:2024/06/18 13:10

1.AVL树的定义

  • 平衡二叉树是一种高度平衡的二叉排序树(父节点大于左子树、小于右子树)。

  • 平衡二叉树的每一个节点的左子树和右子树的高度差至多等于1 。

  • 二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF ,取值为-1,0,或1

  • 最小不平衡子树:距离插入结点最近的,且平衡因子的绝对值大于1 的结点为根的子树

这里写图片描述

这里写图片描述

这里写图片描述

2.AVL树失衡调整

数组a[]={4,5,6,3,2,8,7,0,1}构建一棵AVL树

(1)左单旋转(BF<-1)

首先插入{4,5,6},在插入元素6后出现不平衡的情况:

这里写图片描述

单左旋代码

    public Node leftRotation(Node root) {        Node p = root.right;        root.right = p.left;        p.left = root;        return p;    }

(2)右单旋转

插入元素{3,2},出现了不平衡的情况。

这里写图片描述

单右旋代码:

    public Node rightRotation(Node root) {        Node p = root.left;        root.left = p.right;        p.right = root;        return p;    }

(3)先右旋后左旋

继续插入元素{8,7},第一次旋转后,AVL树仍旧处于不平衡的状态,第二次旋转再次进行调整。即BF一负一正。

这里写图片描述

(4)先左旋后右旋

接着插入节点{0,1},首先对最小不平衡子树的根节点(也就是节点2)的左孩子(也就是0)进行左旋操作,再对节点2进行一次右旋操作。即BF一正一负。

这里写图片描述

3.AVL树的插入与删除

(1)插入

//将结点插入到AVL树中,并返回根节点private AVLTreeNode<T> insert(AVLTreeNode<T> tree, T key) {    if (tree == null) {        // 新建节点        tree = new AVLTreeNode<T>(key, null, null);        if (tree==null) {            System.out.println("ERROR: create avltree node failed!");            return null;        }    } else {        int cmp = key.compareTo(tree.key);           if (cmp < 0) {    // 应该将key插入到"tree的左子树"的情况            tree.left = insert(tree.left, key);            // 插入节点后,若AVL树失去平衡,则进行相应的调节。            if (height(tree.left) - height(tree.right) == 2) {                if (key.compareTo(tree.left.key) < 0)                    tree = leftLeftRotation(tree);                else                    tree = leftRightRotation(tree);            }        } else if (cmp > 0) {    // 应该将key插入到"tree的右子树"的情况            tree.right = insert(tree.right, key);            // 插入节点后,若AVL树失去平衡,则进行相应的调节。            if (height(tree.right) - height(tree.left) == 2) {                if (key.compareTo(tree.right.key) > 0)                    tree = rightRightRotation(tree);                else                    tree = rightLeftRotation(tree);            }        } else {    // cmp==0            System.out.println("添加失败:不允许添加相同的节点!");        }    }    tree.height = max( height(tree.left), height(tree.right)) + 1;    return tree;}public void insert(T key) {    mRoot = insert(mRoot, key);}

(2)删除

//删除结点(z),返回根节点treeprivate AVLTreeNode<T> remove(AVLTreeNode<T> tree, AVLTreeNode<T> z) {    // 根为空 或者 没有要删除的节点,直接返回null。    if (tree==null || z==null)        return null;    int cmp = z.key.compareTo(tree.key);    if (cmp < 0) {        // 待删除的节点在"tree的左子树"中        tree.left = remove(tree.left, z);        // 删除节点后,若AVL树失去平衡,则进行相应的调节。        if (height(tree.right) - height(tree.left) == 2) {            AVLTreeNode<T> r =  tree.right;            if (height(r.left) > height(r.right))                tree = rightLeftRotation(tree);            else                tree = rightRightRotation(tree);        }    } else if (cmp > 0) {    // 待删除的节点在"tree的右子树"中        tree.right = remove(tree.right, z);        // 删除节点后,若AVL树失去平衡,则进行相应的调节。        if (height(tree.left) - height(tree.right) == 2) {            AVLTreeNode<T> l =  tree.left;            if (height(l.right) > height(l.left))                tree = leftRightRotation(tree);            else                tree = leftLeftRotation(tree);        }    } else {    // tree是对应要删除的节点。        // tree的左右孩子都非空        if ((tree.left!=null) && (tree.right!=null)) {            if (height(tree.left) > height(tree.right)) {                // 如果tree的左子树比右子树高;                // 则(01)找出tree的左子树中的最大节点                //   (02)将该最大节点的值赋值给tree。                //   (03)删除该最大节点。                // 这类似于用"tree的左子树中最大节点"做"tree"的替身;                // 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。                AVLTreeNode<T> max = maximum(tree.left);                tree.key = max.key;                tree.left = remove(tree.left, max);            } else {                // 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1)                // 则(01)找出tree的右子树中的最小节点                //   (02)将该最小节点的值赋值给tree。                //   (03)删除该最小节点。                // 这类似于用"tree的右子树中最小节点"做"tree"的替身;                // 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的。                AVLTreeNode<T> min = maximum(tree.right);                tree.key = min.key;                tree.right = remove(tree.right, min);            }        } else {            AVLTreeNode<T> tmp = tree;            tree = (tree.left!=null) ? tree.left : tree.right;            tmp = null;        }    }    return tree;}public void remove(T key) {    AVLTreeNode<T> z;     if ((z = search(mRoot, key)) != null)        mRoot = remove(mRoot, z);}



本人才疏学浅,若有错,请指出
谢谢!

参考资料:

1.平衡二叉树,AVL树之图解篇
2.数据结构图文解析之:AVL树详解
3.AVL树(三)之 Java的实现