AVL树

参考:https://www.cnblogs.com/skywang12345/p/3577479.html

AVL树本质上还是一棵二叉搜索树,它的特点是:

1.本身首先是一棵二叉搜索树。
2.带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。
也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。

以下为四种不平衡的临界条件,分别为LL(leftleft),LR(leftright),RL,RR。


当每次到达这四种不平衡的临界条件时,都进行旋转树操作,那么树就能维持平衡:

private AVLTreeNode leftLeftRotation(AVLTreeNode node) {
        checkNull(node);

        AVLTreeNode  leftChild = node.left;
        node.left =  leftChild.right;
         leftChild.right = node;
        //reset the height
        node.height = max(height(node.left), height(node.right)) + 1;
         leftChild.height = max(height( leftChild.left), height( leftChild.right)) + 1;
        return  leftChild;
    }

    private AVLTreeNode rightRightRotation(AVLTreeNode node) {
        checkNull(node);

        AVLTreeNode  rightChild = node.right;
        node.right =  rightChild.left;
         rightChild.left = node;

        node.height = max(height(node.left), height(node.right)) + 1;
         rightChild.height = max(height( rightChild.left), height( rightChild.right)) + 1;
        return  rightChild;
    }

    private AVLTreeNode leftRightRotation(AVLTreeNode node) {
        node.left = rightRightRotation(node.left);//先对node.left旋转
        return leftLeftRotation(node);//后对自己旋转
    }

    private AVLTreeNode rightLeftRotation(AVLTreeNode node) {
        node.right = leftLeftRotation(node.right);
        return rightRightRotation(node);
    }

结合插入和删除,AVL树的实现:

package DataStructureAndAlgor;
/**
 * AVL树
 * 回顾的时候,最好把LL,RR,LR,RL旋转的四张图画出来,好理解。
 *
 * @param <T>
 */
public class AVLTree<T extends Comparable<T>> {

    class AVLTreeNode {
        T key;
        int height;
        AVLTreeNode left;
        AVLTreeNode right;

        public AVLTreeNode(T key, AVLTreeNode left, AVLTreeNode right) {
            this.key = key;
            this.left = left;
            this.right = right;
            this.height = 0;//初始化的结点的height都是0,因为都是作为叶子结点添加到树中的。
        }
    }

    private AVLTreeNode mRoot;


    private int height(AVLTreeNode tree) {
        if (tree != null) {
            return tree.height;
        }
        return 0;
    }

    public int height() {
        return height(mRoot);
    }

    public int height(T key) {
        AVLTreeNode node = search(mRoot, key);
        return height(node);
    }

    private int max(int a, int b) {
        return a > b ? a : b;
    }

    private AVLTreeNode leftLeftRotation(AVLTreeNode node) {
        checkNull(node);

        AVLTreeNode  leftChild = node.left;
        node.left =  leftChild.right;
         leftChild.right = node;
        //reset the height
        node.height = max(height(node.left), height(node.right)) + 1;
         leftChild.height = max(height( leftChild.left), height( leftChild.right)) + 1;
        return  leftChild;
    }

    private AVLTreeNode rightRightRotation(AVLTreeNode node) {
        checkNull(node);

        AVLTreeNode  rightChild = node.right;
        node.right =  rightChild.left;
         rightChild.left = node;

        node.height = max(height(node.left), height(node.right)) + 1;
         rightChild.height = max(height( rightChild.left), height( rightChild.right)) + 1;
        return  rightChild;
    }

    private AVLTreeNode leftRightRotation(AVLTreeNode node) {
        node.left = rightRightRotation(node.left);//先对node.left旋转
        return leftLeftRotation(node);//后对自己旋转
    }

    private AVLTreeNode rightLeftRotation(AVLTreeNode node) {
        node.right = leftLeftRotation(node.right);
        return rightRightRotation(node);
    }

    private void checkNull(AVLTreeNode node) {
        if (node == null) {
            throw new NullPointerException();
        }
    }

    /**
     * 将结点插入到AVL树种,并返回根节点。
     *
     * @param tree AVL树的根节点
     * @param key  待插入的结点的值。
     * @return 根节点
     */
    private AVLTreeNode insert(AVLTreeNode tree, T key) {
        if (tree == null) {//递归终点
            tree = new AVLTreeNode(key, null, null);
        } else {
            int cmp = key.compareTo(tree.key);//compare result

            if (cmp < 0) {
                tree.left = insert(tree.left, key);//递归插入
                //插入结点后,若AVLTree失去平衡,旋转树。
                if (height(tree.left) - height(tree.right) == 2) {//说明是tree结点出问题(必须是left-right,否则得负值)
                    if (key.compareTo(tree.left.key) < 0) {//这里如果写成tree.right.key会报空指针异常
                        tree = leftLeftRotation(tree);
                    } else {
                        tree = leftRightRotation(tree);
                    }
                }
            } else if (cmp > 0) {
                tree.right = insert(tree.right, key);//递归插入
                if (height(tree.right) - height(tree.left) == 2) {//说明是tree结点出问题(必须是left-right,否则得负值)
                    if (key.compareTo(tree.right.key) < 0) {//这里如果写成tree.left.key会报空指针异常
                        tree = rightLeftRotation(tree);
                    } else {
                        tree = rightRightRotation(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);
    }

    /**
     * 删除树中的结点z, 返回根节点
     *
     * @param tree 从tree结点开始找寻要删除的结点z
     * @param z    待删除的根节点
     * @return 根节点
     */
    private AVLTreeNode remove(AVLTreeNode tree, AVLTreeNode z) {
        if (tree == null || z == null) {
            return null;
        }

        int cmp = z.key.compareTo(tree.key);
        if (cmp < 0) {//待删除的结点在tree的左子树
            tree.left = remove(tree.left, z);
            if (height(tree.right) - height(tree.left) == 2) {//如果删除后tree失去平衡, 进行调节
                AVLTreeNode 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);
            if (height(tree.left) - height(tree.right) == 2) {//失去平衡
                AVLTreeNode l = tree.left;
                if (height(l.left) < height(l.right)) {
                    tree = leftRightRotation(tree);
                } else {
                    tree = leftLeftRotation(tree);
                }
            }
        } else {//cmp==0,tree是要删除的结点
            if (tree.left != null && tree.right != null) {//tree的左右孩子非空
                if (height(tree.left) > height(tree.right)) {
                    /*
                    如果tree的左子树比右子树高,则
                    (1)找出tree的左子树中的最大结点
                    (2)将该最大结点的值赋值给tree
                     (3) 删除该最大结点 。
                     采用该方法的好处是:删除结点之后,AVL树仍然是平衡的。
                     */
                    AVLTreeNode max = maximum(tree.left);
                    tree.key = max.key;
                    tree.left = remove(tree.left, max);
                } else {
                    /*
                    如果tree的右子树比左子树高或相等,则
                    (1) 找出tree的右子树中的最小结点
                    (2) 将该最小结点的值赋值给tree
                     (3) 删除该最大结点
                    采用该方法的好处是:删除结点之后,AVL树仍然是平衡的。
                     */
                    AVLTreeNode min = minimum(tree.right);
                    tree.key = min.key;
                    tree.right = remove(tree.right, min);
                }
            } else {
                tree = (tree.left != null) ? tree.left : tree.right;
            }
        }

        if (tree != null) {//删除叶子结点的时候,这个tree是会返回null的。
            tree.height = max(height(tree.left), height(tree.right)) + 1;
        }


        return tree;
    }

    public void remove(T key) {
        AVLTreeNode z = search(mRoot, key);
        if (z != null) {
            mRoot = remove(mRoot, z);
        }
    }

    private void preOrderPrint(AVLTreeNode root, int depth) {
        if (root != null) {
            System.out.println();//换行
            for (int i = 0; i < depth; i++) {//for循环来打印value前的空格
                System.out.print("--");//结点深度越大,空格越多
            }
            System.out.print(root.key);
            depth++;
            preOrderPrint(root.left, depth);
            preOrderPrint(root.right, depth);
        }
    }

    public void print() {
        preOrderPrint(mRoot, 0);
    }


    private AVLTreeNode search(AVLTreeNode node, T key) {
        if (node == null) {
            return null;
        }
        int cmp = key.compareTo(node.key);
        if (cmp < 0) {
            return search(node.left, key);
        } else if (cmp > 0) {
            return search(node.right, key);
        } else {
            return node;
        }

    }

    private AVLTreeNode maximum(AVLTreeNode tree) {
        if (tree == null) {
            return null;
        }
        while (tree.right != null) {
            tree = tree.right;
        }
        return tree;
    }

    private AVLTreeNode minimum(AVLTreeNode tree) {
        if (tree == null) {
            return null;
        }
        while (tree.left != null) {
            tree = tree.left;
        }
        return tree;
    }

    public T maximum() {
        AVLTreeNode p = maximum(mRoot);
        if (p != null) {
            return p.key;
        }
        return null;
    }

    public T minimum() {
        AVLTreeNode p = minimum(mRoot);
        if (p != null) {
            return p.key;
        }
        return null;
    }
}

测试:

public class Main {
    public static void main(String[] args) {
        AVLTree<Integer> tree = new AVLTree<>();
        int arr[] = {33, 22, 4, 7, 0, 60, 13, 32, 21, 99, 6, 15, 27, 2};

        for (int i = 0; i < arr.length; i++) {
            tree.insert(arr[i]);
        }

        tree.print();
        System.out.println();
        System.out.println("树的根结点高度:" + tree.height());

        System.out.println("删除结点33,32,27,60,99");

        tree.remove(33);
        tree.remove(32);
        tree.remove(27);
        tree.remove(60);
        tree.remove(99);

        tree.print();
        System.out.println();
        System.out.println("树的根结点高度:" + tree.height());
    }
}

打印:

22
--7
----4
------0
--------2
------6
----15
------13
------21
--33
----32
------27
----60
------99
树的根结点高度:5
99
0
删除结点33,32,27,60,99

7
--4
----0
------2
----6
--15
----13
----22
------21
树的根结点高度:4
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容