数据结构:AVL树

177 阅读2分钟

自平衡二分搜索树

特点

除了同上篇的二分搜索树, 还有一个重要的特点:任意节点的平衡因子的绝对值不能大于1

  • 高度:我们定义叶子节点的高度为1, 叶子节点的子节点(null)高度为0; 父节点的高度为左右子节点中的最大高度加1: max(height(left), height(right)) + 1。
  • 平衡因子:节点的左右子节点高度差
  • 储存的数据, 依然是K具有可比较性。

实现代码

public class AVLTree<K extends Comparable<E>, V> {
        private class Node {
            public K key;
            public V value;
            public Node left; 
            public Node right;
            // 描述节点高度
            public int height;
            
            public Node(K key, V value, Node left, Node right, int height) {
                this.key = key;
                this.value = value;
                this.left = left;
                this.right = right;
                this.height = height;
            }
            public Node(K key, V value) {
                // 默认构造左右子节点都为空, 高度为1
                this(key, value, null, null, 1)
            }
        }
        
        private Node root;
        private int size;
        
        public AVLTree() {
            root = null;
            size = 0;
        }
        
        public int getSize() {
            return size;
        }
        public boolean isEmpty() {
            return size == 0;
        }
        
        // 获取高度
        public int getHeight(Node node) {
            return node == null ? 0 : node.height;
        }
        
        // 获取平衡因子
        public int getBanlanceFactor(Node node)){
            if (node == null) return 0;
            return getHeight(node.left) - getHeight(node.right)
        }
        
        // 节点左旋
        // 节点逆时针方向旋转,同时保持树的特性
        // 旋转节点的 右子节点的 左子节点 赋值为 旋转节点的右子节点
        // 旋转节点的 右子节点的 左子节点 赋值为旋转节点
        // 图例(旋转节点 y):
          //    y                             x
          //  /  \                          /   \
          // T1   x      向左旋转 (y)       y     z
          //     / \   - - - - - - - ->   / \   / \
          //   T2  z                     T1 T2 T3 T4
          //      / \
          //     T3 T4
        
        private Node leftRotate(Node y) {
            Node x = y.right;
            // 两步
            y.right= x.left;
            x.left = y;
            // 维护x,y的高度, 先维护子节点的高度
            y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
            x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
            return x;
        }
        
        // 节点右旋
        private Node rightRotate(Node y) {
            Node x = y.left;
            // 两步
            y.left= x.right;
            x.right = y;
            // 维护x,y的高度, 先维护子节点的高度
            y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
            x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
            return x;
        }
        
        // 添加元素
        public void add(K key, V value) {
            root = add(root, key, value);
        }
        private Node add(Node node, K key, V value) {
            if (node == null) {
                size++;
                return new Node(key, value);
            }
            if (key.compareTo(node.key) > 0) {
                node.right = add(node.right, key, value)
            }else if (key.compareTo(node.key) < 0) {
                 node.left = add(node.left, key, value)
            }else {
                node.value = value;
            }
            // 维护高度
            node.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1
            // 检测 node 的平衡性
            if (getBanlanceFactor(node) > 1) {
                // 树整体向左倾斜, 需要右旋
                // 但是考虑一种情况:
                // node的左节点的右子节点高度大于node的左节点的左子节点高度,也就是平衡因子小于0,
                // 但是不会小于 -1,因为node的右子节点肯定是平衡的
                // 此时需要先把node左节点左旋
                if (getBanlanceFactor(node.left) < 0) {
                    node.left = leftRotate(node.left);
                }
                node = rightRotate(node);
            }else if (getBanlanceFactor(node) < -1) {
                // 树整体向右倾斜, 需要左旋, 同理, 也要考虑上面那种情况
                if (getBanlanceFactor(node.right) > 0) {
                    node.rightt = rightRotate(node.right);
                }
                node = leftRotate(node);
            }
            return node;
        }
        
        // 删除节点
        public V remove(K key) {
            Node retNode = getNode(key);
            if (retNode != null) {
                root = remove(root, K key);
                
                return retNode.value;    
            }
            return null;
        }
        private Node remove(Node node, K key) {
            if (node == null) { 
                return null;    
            }
            Node ret = null;
            if (key.compareTo(node.key) > 0) {
                node.right = remove(node.right, key)
            }else if (key.compareTo(node.key) < 0) {
                node.left = remove(node.left, key)
            }else {
                // 实现代码
                if (node.left == null) {
                    ret = node.right;
                    // 释放空间
                    node.right = null;
                    size--;
                }else if (node.right == null) {
                    ret = node.left;
                    // 释放空间
                    node.left = null;
                    size--;
                }else {
                    Node succeedNode = minimum(node.right);
                    succeedNode.left = node.left;
                    succeedNode.right = remove(node.right, succeedNode.key)
                    node.left = node.right = null;
                    ret = succeedNode;   
                }
                Node node = ret;
                 // 维护高度
                node.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1
                // 检测 node 的平衡性
                if (getBanlanceFactor(node) > 1) {
                    // 树整体向左倾斜, 需要右旋
                    // 但是考虑一种情况:
                    // node的左节点的右子节点高度大于node的左节点的左子节点高度,也就是平衡因子小于0,
                    // 但是不会小于 -1,因为node的右子节点肯定是平衡的
                    // 此时需要先把node左节点左旋
                    if (getBanlanceFactor(node.left) < 0) {
                        node.left = leftRotate(node.left);
                    }
                    node = rightRotate(node);
                }else if (getBanlanceFactor(node) < -1) {
                    // 树整体向右倾斜, 需要左旋, 同理, 也要考虑上面那种情况
                    if (getBanlanceFactor(node.right) > 0) {
                        node.rightt = rightRotate(node.right);
                    }
                    node = leftRotate(node);
                }
                return ret
            }
        }
        
        // 查找某个node, 整体和add很相似
        private Node getNode(K key) {
            if (key.compareTo(node.key) > 0) {
                return getNode(node.right, key)
            }else if (key.compareTo(node.key) < 0) {
                return getNode(node.left, key)
            }else {
                return node;
            }
        }
        
        // 找到节点的后继节点
        private Node minimum(Node node) {
            while (node.left != null) {
                node = node.left;
            }
            return node;
        }
        
        // 查看是否包含某个key
        public boolean contains(K key) {
            return contains(root, key)
        }
        private boolean contains(Node node, K key) {
            if (node == null) {
                return false;
            }
            if (key.compareTo(node.key) > 0) {
                return contains(node.right, key)
            }else if (key.compareTo(node.key) < 0) {
                return contains(node.left, key)
            }else {
                return true;
            }
        }
    }