• 【墨鳌】【数据结构】【AVL树】


    AVL Tree

    • Binary Search Tree 现有属性之上,依赖于可以其二分查找的特性,进行树高的调整优化
    • 在每个节点多维护一个子树高度(height)的信息
    • 每次 insert/remove 时,按照限制条件,动态旋转,以满足任意节点的平衡因子的绝对值 \(<=1\)

    节点属性

    • key - 可以比较的对象类型
    • left,right - 左右儿子节点
    • height - 子树高度
        public class AVLNode<Key> {
            public Key key;
            public AVLNode<Key> left, right;
            int height;
            ...
        }
    

    AVLTree 核心部分

    • root - 根节点
    • insert - 插入 key
    • remove - 移除 key
    • balance - 姿态调整

    姿态调整

    LL型姿态:X右旋

    image-20220408210427631 image-20220408210725023

    RR型姿态:X左旋

    image-20220408210131025 image-20220408210817298

    LR型姿态:Y左旋 再 X右旋

    image-20220408211154186 image-20220408211758142

    RL型姿态:Y右旋 再 X左旋

    image-20220408211414594 image-20220408211758142

    插入操作

    • 普通二分查找到元素需要插入的位置,添加新的结点
    • 重新计算新的 height,以及平衡因子
    • 遇到不平衡直接进行姿态调整

    删除操作

    • 相较于插入会复杂一点,核心在于找删除元素的前驱/后继结点
    • 同插入,先搜索到相应位置
    • 如果,为 leaf 结点了,直接删除,不影响树的平衡
    • 如果,为中间结点,有前驱(或者后继),选择其一,交换其 key 到当前结点
      • 删除前驱/后继(此时必然是删除 leaf 结点),然后根据新 height 调整姿态即可

    测试与使用

    • 结合上一篇文章
    • 与 BST 对比测试
    package trees;
    
    
    import trees.objects.AVLTree;
    import trees.objects.BinaryTree;
    
    import java.util.Arrays;
    import java.util.List;
    
    
    public class Tests {
        public static void main(String[] args) {
            AVLTree<Integer> avl=new AVLTree<>();
            BinaryTree<Integer,Integer> bst=new BinaryTree<>();
            List<Integer> keys = Arrays.asList(1, 19, 30, 36, 50, 89, 101, 40, 90, 105, 103);
            for (int key : keys) {
                bst.put(key,key);
                avl.insert(key);
                System.out.println("BST:");
                bst.printTree();
                System.out.println("AVL Tree:");
                avl.printTree();
                System.out.println("**************************************");
            }
        }
    }
    

    完整Java代码实现

    点击此处,访问GitHub代码仓库

    public class AVLTree<T extends Comparable<T>> {
    
        private static final int MAX_HEIGHT_DIFFERENCE = 1;
    
        private AVLNode<T> root;
    
        public class AVLNode<Key> {
            public Key key;
            public AVLNode<Key> left, right;
    
            int height;
    
            public AVLNode(Key key, AVLNode<Key> left, AVLNode<Key> right) {
                this.key = key;
                this.left = left;
                this.right = right;
                this.height = 1;
            }
        }
    
        public AVLTree() {
            root = null;
        }
    
        public AVLTree(T... keys) {
            if (keys == null || keys.length < 1) {
                throw new NullPointerException();
            }
    
            root = new AVLNode<>(keys[0], null, null);
            for (int i = 1; i < keys.length && keys[i] != null; i++) {
                root = insert(root, keys[i]);
            }
        }
    
        public T find(T key) {
            if (key == null || root == null) {
                return null;
            }
            return find(root, key, key.compareTo(root.key));
        }
    
        private T find(AVLNode<T> node, T key, int cmp) {
            if (node == null) {
                return null;
            }
    
            if (cmp == 0) {
                return node.key;
            }
    
            return find(
                    (node = cmp > 0 ? node.right : node.left),
                    key,
                    node == null ? 0 : key.compareTo(node.key));
        }
    
        public void insert(T key) {
            if (key == null) {
                throw new NullPointerException();
            }
            root = insert(root, key);
        }
    
        private AVLNode<T> insert(AVLNode<T> node, T key) {
            if (node == null) {
                return new AVLNode<>(key, null, null);
            }
    
            int cmp = key.compareTo(node.key);
            if (cmp == 0) {
                return node;
            }
            if (cmp < 0) {
                node.left = insert(node.left, key);
            } else {
                node.right = insert(node.right, key);
            }
    
            if (Math.abs(height(node.left) - height(node.right)) > MAX_HEIGHT_DIFFERENCE) {
                node = balance(node);
            }
            refreshHeight(node);
            return node;
        }
    
        private int height(AVLNode<T> node) {
            if (node == null) {
                return 0;
            }
            return node.height;
        }
    
        private void refreshHeight(AVLNode<T> node) {
            node.height = Math.max(height(node.left), height(node.right)) + 1;
        }
    
        private AVLNode<T> balance(AVLNode<T> node) {
            AVLNode<T> node1, node2;
            // ll
            if (height(node.left) > height(node.right) &&
                    height(node.left.left) >= height(node.left.right)) {
                node1 = node.left;
                node.left = node1.right;
                node1.right = node;
    
                refreshHeight(node);
                return node1;
            }
            // lr
            if (height(node.left) > height(node.right) &&
                    height(node.left.right) > height(node.left.left)) {
                node1 = node.left;
                node2 = node.left.right;
                node.left = node2.right;
                node1.right = node2.left;
                node2.left = node1;
                node2.right = node;
    
                refreshHeight(node);
                refreshHeight(node1);
                return node2;
            }
            // rr
            if (height(node.right) > height(node.left) &&
                    height(node.right.right) >= height(node.right.left)) {
                node1 = node.right;
                node.right = node1.left;
                node1.left = node;
    
                refreshHeight(node);
                return node1;
            }
            // rl
            if (height(node.right) > height(node.left) &&
                    height(node.right.left) > height(node.right.right)) {
                node1 = node.right;
                node2 = node.right.left;
                node.right = node2.left;
                node1.left = node2.right;
                node2.left = node;
                node2.right = node1;
    
                refreshHeight(node);
                refreshHeight(node1);
                return node2;
            }
            return node;
        }
    
        public void remove(T key) {
            if (key == null) {
                throw new NullPointerException();
            }
            root = remove(root, key);
        }
    
        private AVLNode<T> remove(AVLNode<T> node, T key) {
            if (node == null) {
                return null;
            }
    
            int cmp = key.compareTo(node.key);
            if (cmp < 0) {
                node.left = remove(node.left, key);
            }
            if (cmp > 0) {
                node.right = remove(node.right, key);
            }
            if (cmp == 0) {
                if (node.left == null || node.right == null) {
                    return node.left == null ? node.right : node.left;
                }
                T successorKey = successorOf(node).key;
                node = remove(node, successorKey);
                node.key = successorKey;
            }
    
            if (Math.abs(height(node.left) - height(node.right)) > MAX_HEIGHT_DIFFERENCE) {
                node = balance(node);
            }
            refreshHeight(node);
            return node;
        }
    
        private AVLNode<T> successorOf(AVLNode<T> node) {
            if (node == null) {
                throw new NullPointerException();
            }
            if (node.left == null || node.right == null) {
                return node.left == null ? node.right : node.left;
            }
    
            return height(node.left) > height(node.right) ?
                    findMax(node.left, node.left.right, node.left.right == null) :
                    findMin(node.right, node.right.left, node.right.left == null);
        }
    
        private AVLNode<T> findMax(AVLNode<T> node, AVLNode<T> right, boolean rightIsNull) {
            if (rightIsNull) {
                return node;
            }
            return findMax((node = right), node.right, node.right == null);
        }
    
        private AVLNode<T> findMin(AVLNode<T> node, AVLNode<T> left, boolean leftIsNull) {
            if (leftIsNull) {
                return node;
            }
            return findMin((node = left), node.left, node.left == null);
        }
    
        // -------------------------- Print Tree --------------------------
    
        private void printTree(AVLNode node, String prefix, boolean isLeft) {
            if (node == null) {
                System.out.println("Empty tree");
                return;
            }
    
            if (node.right != null) {
                printTree(node.right, prefix + (isLeft ? "│   " : "    "), false);
            }
    
            System.out.println(prefix + (isLeft ? "└── " : "┌── ") + node.key);
    
            if (node.left != null) {
                printTree(node.left, prefix + (isLeft ? "    " : "│   "), true);
            }
        }
    
        private void printTree(AVLNode node) {
            printTree(node, "", true);
        }
    
        public void printTree() {
            System.out.println(" >> START TO PRINT THE TREE:");
            printTree(root);
            System.out.println(" << END TO PRINT THE TREE");
        }
    }
    
  • 相关阅读:
    遥控器拆卸记录
    计算器拆卸记录
    no matching constructor for initialization
    STL
    排序方法
    二叉树之广度优先遍历
    C++之queue学习记录
    方向电路
    站间联系电路
    求二叉树的最大深度
  • 原文地址:https://www.cnblogs.com/JasonCow/p/16120013.html
Copyright © 2020-2023  润新知