20_有序表

About 13 minstudyalgorithm

一、AVL

1、介绍

avl树是一种平衡二叉树,在删除和新增元素的时候利用左旋和右旋的方式将树调整平衡,左树的高度和右树的高度差不超过1。这种树的结构建立好之后能有效的提高在应对范围查询时的效率,GPT:

AVL树是一种自平衡的二叉搜索树,它的命名来源于其发明者Adelson-Velskii和Landis。AVL树具有以下特点:

每个节点都有一个表示平衡因子的整数值,用来衡量左右子树的高度差。
AVL树中的任意节点的左右子树高度最多相差1。
当插入或删除操作使得树不再满足平衡条件时,AVL树会通过旋转操作来进行自平衡。
通过对插入和删除操作进行适当的旋转,AVL树能够始终保持近似平衡状态,以提供较快的查找、插入和删除操作。由于其平衡性质,AVL树在大部分情况下能够保证较好的性能,并且没有退化为链表的风险。

需要注意的是,AVL树并非唯一的自平衡二叉搜索树,还存在其他类型的树结构,如红黑树等。这些树结构在实际应用中可以根据具体需求选择合适的数据结构。

2、思路

在新增和删除元素的时候都需要将树调整平衡,最重要的就是平衡逻辑

1、左旋和右旋就是将左子节点或者右子节点变为当前root节点,改变指针指向即可

avl

2、调整平衡的主要逻辑是判断左树和右树的高度差大于1时则调整对应子节点

  • 左树高
    • 左树的左子节点大于等于左树的右子节点:右旋
    • 左树的左子节点小于左树的右子节点:左树的左子节点左旋后在右旋
  • 右树高
    • 左树高度大于右树,则当前节点左旋
    • 左树高度小于右树,右树右旋后当前节点左旋

3、删除节点的逻辑场景也比较多

  • 删除key大于当前节点则去右边删除节点
  • 删除key小于当前节点则去左边删除节点
  • 等于当前key则需要判断当前节点的左右子节点是否存在,相应处理后将树调平即可
    • 左右子节点都不存在则直接删除
    • 只存在左节点则将当前节点指向左节点
    • 只存在右节点则将当前节点指向右节点
    • 两个都存在:用临时节点记录最左节点,之后删除最左节点后。当前节点赋值为临时节点

3、代码

// avl树
    public static class AVLTreeMap<K extends Comparable<K>, V> {

        private AVLNode<K, V> root;
        public int size;

        public AVLTreeMap() {
            this.root = null;
            this.size = 0;
        }
        // 获取大小
        public int size() {
            return size;
        }
        // 判断关键字是否包含
        public boolean containsKey(K k) {
            if(k == null) {
                return false;
            }
            AVLNode<K, V> lastIndex = findLastIndex(k);
            return lastIndex != null && k.compareTo(lastIndex.k) == 0 ? true: false;
        }

        // 往树里面添加值
        public void put(K k, V v) {
            if(k == null) {
                return;
            }

            AVLNode<K, V> lastIndex = findLastIndex(k);
            if(lastIndex != null && k.compareTo(lastIndex.k) == 0) {
                lastIndex.v= v;
            } else {
                size++;
                root = add(root, k, v);
            }
        }

        // 删除key
        public void remove(K key) {
            if (key == null) {
                return;
            }
            if (containsKey(key)) {
                size--;
                // 重新将root指向一下,可能删除头
                root = delete(root, key);
            }
        }

        // 获取元素
        public V get(K key) {
            if (key == null) {
                return null;
            }
            AVLNode<K, V> lastNode = findLastIndex(key);
            if (lastNode != null && key.compareTo(lastNode.k) == 0) {
                return lastNode.v;
            }
            return null;
        }
        // 获取第一个元素,最左节点
        public K firstKey() {
            if (root == null) {
                return null;
            }
            AVLNode<K, V> cur = root;
            while (cur.l != null) {
                cur = cur.l;
            }
            return cur.k;
        }
        // 获取最后一个元素,最右节点
        public K lastKey() {
            if (root == null) {
                return null;
            }
            AVLNode<K, V> cur = root;
            while (cur.r != null) {
                cur = cur.r;
            }
            return cur.k;
        }

        // 返回小于或等于给定键的最大键,如果没有这样的键,则null
        public K floorKey(K k) {
            if(k == null){
                return null;
            }
            AVLNode<K,V> result =  null;
            AVLNode<K,V> cur = root;
            while (cur != null) {
                if(k.compareTo(cur.k) == 0) {
                    result = cur;
                    break;
                }
                // 小于k的则往左找,否则往右找
                if(k.compareTo(cur.k) < 0) {
                    cur = cur.l;
                } else {
                    result = cur;
                    cur=cur.r;
                }
            }

            return result == null ? null : result.k;
        }

        // 返回大于或等于给定键的最小键,如果没有这样的键,则null
        public K ceilingKey(K key) {
            if(key == null){
                return null;
            }
            AVLNode<K, V> result = null;
            AVLNode<K, V> cur = root;
            while (cur != null) {
                if (key.compareTo(cur.k) == 0) {
                    result = cur;
                    break;
                } else if (key.compareTo(cur.k) < 0) {
                    result = cur;
                    cur = cur.l;
                } else {
                    cur = cur.r;
                }
            }
            return result == null ? null : result.k;
        }

        // 右旋
        private AVLNode<K, V> rightRotate(AVLNode<K, V> cur) {
            AVLNode<K, V> left = cur.l;
            // 旋转前左节点指向旋转后的右节点
            cur.l = left.r;
            // 旋转后的右节点指向旋转前的节点
            left.r = cur;

            // 计算的高度,左右两边最大的高度加一
            setHeight(cur);
            setHeight(left);
            return left;
        }

        // 左旋
        private AVLNode<K,V> leftRotate(AVLNode<K, V> cur) {
            AVLNode<K,V> right = cur.r;
            cur.r = right.l;
            right.l = cur;
            // 计算高度
            setHeight(cur);
            setHeight(right);
            return right;
        }

        // AVL保持平衡
        private AVLNode<K, V> maintain(AVLNode<K, V> cur) {
            if(cur == null) {
                return null;
            }
            int lh = cur.l != null ? cur.l.height : 0;
            int rh = cur.r != null ? cur.r.height : 0;
            // 左树和右树的高度大于1才进行调整
            if(Math.abs(lh - rh) < 2) {
                return cur;
            }

            // 左树高
            if(lh > rh) {
                int llh = cur.l != null && cur.l.l != null ? cur.l.l.height : 0;
                int lrh = cur.l != null && cur.l.r != null ? cur.l.r.height : 0;
                // 左树高度大于右树,则当前节点右旋
                if(llh >= lrh) {
                    cur = rightRotate(cur);
                }
                // 左树高度小于右树,左树左旋后当前节点右旋
                else {
                    cur.l = leftRotate(cur.l);
                    cur = rightRotate(cur);
                }
            }
            // 右树高
            else {
                int rlh = cur.r != null && cur.r.l != null ? cur.r.l.height : 0;
                int rrh = cur.r != null && cur.r.r != null ? cur.r.r.height : 0;
                // 左树高度大于右树,则当前节点左旋
                if(rrh >= rlh) {
                    cur = leftRotate(cur);
                }
                // 左树高度小于右树,右树右旋后当前节点左旋
                else {
                    cur.r = rightRotate(cur.r);
                    cur = leftRotate(cur);
                }
            }
            return cur;
        }

        // 给节点设置高度
        private void setHeight(AVLNode<K, V> cur) {
            // 计算的高度,左右两边最大的高度加一
            cur.height = Math.max(cur.l == null ? 0 : cur.l.height, cur.r == null ? 0 : cur.r.height) + 1;
        }

        // 往AVL节点添加值
        private AVLNode<K,V> add(AVLNode<K, V> cur, K k, V v) {
            if(cur == null) {
                return new AVLNode<>(k, v);
            }

            // 增加的值小于当前节点,放左边
            if(k.compareTo(cur.k) < 0) {
                cur.l = add(cur.l, k, v);
            }
            // 放右边
            else {
                cur.r = add(cur.r, k, v);
            }
            setHeight(cur);
            return maintain(cur);
        }

        // 删除k节点,返回删除的节点
        private AVLNode<K, V> delete(AVLNode<K, V> cur, K k) {
            // 大于当前节点去右边删除
            if(k.compareTo(cur.k) > 0) {
                cur.r = delete(cur.r, k);
                setHeight(cur);
                return maintain(cur);
            }
            // 小于当前节点则去左边删除
            if(k.compareTo(cur.k) < 0) {
                cur.l = delete(cur.l, k);
                setHeight(cur);
                return maintain(cur);
            }

            // 删除的节点没有左右子节点
            if(cur.l == null && cur.r == null) {
                cur = null;
                return null;
            }
            // 存在左边的子节点
            if(cur.l != null && cur.r == null) {
                cur = cur.l;
                setHeight(cur);
                return maintain(cur);
            }
            // 存在右边的子节点
            if(cur.l == null && cur.r != null ) {
                cur = cur.r;
                setHeight(cur);
                return maintain(cur);
            }
            // 左右两边都存在子节点
            AVLNode<K, V> des = cur.r;
            // 获取当前右节点的最左节点
            while (des.l != null) {
                des = des.l;
            }

            // 删除最左节点后,将树调平衡
            cur.r = delete(cur.r, des.k);
            // 替换当前节点
            des.l = cur.l;
            des.r = cur.r;
            cur = des;
            setHeight(cur);
            return maintain(cur);
        }
        // 通过key检索到节点
        private AVLNode<K, V> findLastIndex(K k) {
            AVLNode<K, V> pre = root;
            AVLNode<K, V> cur = root;
            while (cur != null) {
                pre = cur;
                if(k.compareTo(cur.k) == 0) {
                    break;
                }
                // 小于k则左树寻找
                if(k.compareTo(cur.k) < 0) {
                    cur = cur.l;
                }
                // 否则右树找
                else {
                    cur = cur.r;
                }
            }
            return pre;
        }
    }
    // avl节点
    public static class AVLNode<K extends Comparable<K>, V> {

        public K k;
        public V v;
        public AVLNode<K, V> l;
        public AVLNode<K, V> r;
        public int height;

        public AVLNode(K key, V val) {
            this.k = key;
            this.v = val;
            height = 1;
        }
    }

二、SizeBalansed

1、介绍

SizeBalansedTree是通过用树的大小来作为维持树平衡的依据,AVL是用树的高度来平衡树,AVL更加的严格。SBT在删除key的时候可以不用调整树平衡,在增加树key的时候会迅速调平。GPT:

“Size-Balanced Tree” (SBT) 和 AVL 树是两种用于实现自平衡二叉搜索树的数据结构。

Size-Balanced Tree 是一种自平衡二叉搜索树,它通过维护每个节点的子树大小来保持平衡。在 SBT 中,每个节点都包含一个额外的属性,即其子树的大小。通过动态调整节点在树中的位置,使得左子树和右子树的大小相对平衡,从而实现整个树的平衡。SBT 支持在 O(log n) 的时间内执行搜索、插入和删除操作。

AVL 树是另一种自平衡二叉搜索树,它通过调整节点的高度来保持平衡。在 AVL 树中,每个节点都包含一个平衡因子,即左子树的高度减去右子树的高度。通过旋转操作和调整平衡因子,AVL 树保持所有节点的平衡因子在 {-1, 0, 1} 范围内,从而实现整个树的平衡。AVL 树也能够在 O(log n) 的时间内执行搜索、插入和删除操作。

两种数据结构的主要区别在于平衡条件的不同。SBT 使用子树大小来平衡树,而 AVL 树使用节点高度来平衡树。另外,SBT 相对于 AVL 树有更低的调整代价,因为它只需要维护子树大小而不是节点高度。然而,AVL 树的平衡条件更为严格,可以提供更加均衡的树结构。

选择使用哪种自平衡二叉搜索树取决于具体的应用场景和需求。如果对树的平衡要求比较高且对性能稍作妥协,AVL 树是一个不错的选择。如果对调整代价更为敏感且对树的平衡要求不那么严格,SBT 可能更适合。

Size-Balanced Tree
Size-Balanced Tree(SBT)是一种自平衡二叉搜索树,旨在通过维护节点的子树大小来实现平衡。

在SBT中,每个节点都有一个额外的属性,即其子树的大小。节点的子树大小是指以该节点为根的子树中包含的节点总数。通过在插入、删除和旋转操作中更新节点的子树大小属性,SBT可以保持整个树的平衡。

SBT的平衡条件是,对于任意节点,其左子树的大小至少为其右子树大小的一半,而且右子树的大小至多为其左子树大小的两倍。这种平衡条件保证了树的高度相对平衡,从而保证了搜索、插入和删除操作的较好性能。

与其他自平衡二叉搜索树(例如AVL树或红黑树)相比,SBT的维护代价较低。因为平衡条件相对宽松,SBT往往会调整更少的节点,这导致了更高的插入和删除性能。

SBT在某些特定场景下表现良好,特别是在频繁进行插入和删除操作的情况下。然而,它并不是通用的数据结构,适用于所有情况。因此,在选择数据结构时,需要根据具体的应用需求和性能要求进行评估和决策。

2、思路

1、左旋和右旋的方式和AVL一致,只是将高度替换为大小

2、调整平衡的时候每次左旋或右旋之后都递归调整平衡

3、删除节点时可以不调整平衡,增加节点时会迅速调平

3、代码

public static class  SizeBalancedTreeMap<K extends Comparable<K>,V> {
        private SBTNode<K, V> root;

        // 左旋
        private SBTNode<K, V> rightRotate(SBTNode<K, V> cur) {
            SBTNode<K, V> l = cur.l;
            cur.l = l.r;
            l.r = cur;
            l.size = cur.size;
            cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
            return l;
        }

        // 右旋
        private SBTNode<K, V> leftRotate(SBTNode<K, V> cur) {
            SBTNode<K, V> r = cur.r;
            cur.r = r.l;
            r.l = cur;
            r.size = cur.size;
            cur.size = (cur.l != null ? cur.l.size : 0) + (cur.r != null ? cur.r.size : 0) + 1;
            return r;
        }

        private SBTNode<K,V> maintain(SBTNode<K, V> cur) {
            if(cur == null) {
                return null;
            }
            // 左树大小
            int ls = cur.l != null ? cur.l.size : 0;
            int lls = cur.l !=null && cur.l.l != null ? cur.l.l.size : 0;
            int lrs = cur.l != null && cur.l.r != null ? cur.l.r.size : 0;
            // 右树大小
            int rs = cur.r != null ? cur.r.size : 0;
            int rls = cur.r != null && cur.r.l != null ? cur.r.l.size : 0;
            int rrs = cur.r != null && cur.r.r != null ? cur.r.r.size : 0;

            // 左左树的树立大于右树的数量
            if(lls > rs) {
                cur = rightRotate(cur);
                // 右边调整平衡
                cur.r = maintain(cur.r);
                cur = maintain(cur);
                return cur;
            }
            // 左右树大于右树
            if(lrs > rs) {
                cur.l = leftRotate(cur.l);
                cur = rightRotate(cur);
                // 调整平衡
                cur.l = maintain(cur.l);
                cur.r = maintain(cur.r);
                cur = maintain(cur);
                return cur;
            }
            if(rrs > ls) {
                cur = leftRotate(cur);
                // 左边调整平衡
                cur.l = maintain(cur.l);
                cur = maintain(cur);
                return cur;
            }
            if(rls > ls) {
                cur.r = rightRotate(cur.r);
                cur = leftRotate(cur);
                // 调整平衡
                cur.l = maintain(cur.l);
                cur.r = maintain(cur.r);
                cur = maintain(cur);
                return cur;
            }
            return cur;
        }

        // 找到树上面k相等的值
        private SBTNode<K, V> findLastIndex(K k) {
            SBTNode<K, V> pre = root;
            SBTNode<K, V> cur = root;
            while (cur != null) {
                pre = cur;
                // 相等
                if(k.compareTo(cur.k) == 0) {
                    break;
                }
                // 去左边找
                else if (k.compareTo(cur.k) < 0) {
                    cur = cur.l;
                }
                // 去右边
                else {
                    cur = cur.r;
                }
            }
            return pre;
        }

        // 离当前值较小的值
        private SBTNode<K, V> findLastNoSmallIndex(K key) {
            SBTNode<K, V> ans = null;
            SBTNode<K, V> cur = root;
            while (cur != null) {
                if (key.compareTo(cur.k) == 0) {
                    ans = cur;
                    break;
                } else if (key.compareTo(cur.k) < 0) {
                    ans = cur;
                    cur = cur.l;
                } else {
                    cur = cur.r;
                }
            }
            return ans;
        }

        // 离当前值较大的值
        private SBTNode<K, V> findLastNoBigIndex(K key) {
            SBTNode<K, V> ans = null;
            SBTNode<K, V> cur = root;
            while (cur != null) {
                if (key.compareTo(cur.k) == 0) {
                    ans = cur;
                    break;
                } else if (key.compareTo(cur.k) < 0) {
                    cur = cur.l;
                } else {
                    ans = cur;
                    cur = cur.r;
                }
            }
            return ans;
        }

        // 添加节点
        private SBTNode<K, V> add(SBTNode<K, V> cur, K k, V v) {
            if(cur == null ) {
                return new SBTNode<>(k, v);
            }
            cur.size++;
            // 左边树添加元素
            if(k.compareTo(cur.k) < 0){
                cur.l = add(cur.l, k, v);
            }
            // 右边树添加元素
            else {
                cur.r = add(cur.r, k, v);
            }
            return maintain(cur);
        }

        // 删除元素
        private SBTNode<K, V> delete(SBTNode<K, V> cur, K k) {
            cur.size--;
            // 左边树删除元素
            if(k.compareTo(cur.k) < 0) {
                cur.l = delete(cur.l, k);
                return cur;
            }
            // 右边树删除元素
            if(k.compareTo(cur.k) > 0) {
                cur.r = delete(cur.r, k);
                return cur;
            }
            // 相等则删除当前元素
            // 左右节点都不为空
            if(cur.l == null && cur.r == null) {
                cur = null;
                return null;
            }
            // 只有左节点,则cur直接指向左节点
            if(cur.l != null && cur.r == null) {
                cur = cur.l;
                return cur;
            }
            // 只有右节点,则cur直接指向右节点
            if(cur.l == null && cur.r != null) {
                cur = cur.r;
                return cur;
            }
            // 删除最左节点后,将树调平衡
            SBTNode<K, V> des = cur.r;
            SBTNode<K, V> pre = null;
            des.size--;
            while (des.l != null) {
                pre = des;
                des = des.l;
                des.size--;
            }
            if(pre != null) {
                pre.l = des.r;
                des.r = cur.r;
            }

            des.l = cur.l;
            des.size = des.l.size + (des.r == null ? 0 : des.r.size) + 1;
            cur = des;
            return cur;
        }

        // 获取
        private SBTNode<K, V> getIndex(SBTNode<K, V> cur, int kth) {
            // 大小匹配到
            if (kth == (cur.l != null ? cur.l.size : 0) + 1) {
                return cur;
            }
            // 小于当前树大小,往左边找
            else if (kth <= (cur.l != null ? cur.l.size : 0)) {
                return getIndex(cur.l, kth);
            }
            // 大于树大小,往右边找
            else {
                return getIndex(cur.r, kth - (cur.l != null ? cur.l.size : 0) - 1);
            }
        }

        // 获取树大小
        public int size() {
            return root == null ? 0 : root.size;
        }

        // 是否包含key
        public boolean containsKey(K key) {
            if (key == null) {
                throw new RuntimeException("invalid parameter.");
            }
            SBTNode<K, V> lastNode = findLastIndex(key);
            return lastNode != null && key.compareTo(lastNode.k) == 0 ? true : false;
        }

        // 新增k v, 修改也是直接put
        public void put(K key, V value) {
            if (key == null) {
                throw new RuntimeException("invalid parameter.");
            }
            // 判断值是否窜
            SBTNode<K, V> lastNode = findLastIndex(key);
            // 存在则进行修改
            if (lastNode != null && key.compareTo(lastNode.k) == 0) {
                lastNode.v = value;
            }
            // 不存在则进行新增
            else {
                root = add(root, key, value);
            }
        }

        // 删除
        public void remove(K key) {
            if (key == null) {
                throw new RuntimeException("invalid parameter.");
            }
            // 判断存在则删除
            if (containsKey(key)) {
                root = delete(root, key);
            }
        }

        // 通过索引获取k
        public K getIndexKey(int index) {
            if (index < 0 || index >= this.size()) {
                throw new RuntimeException("invalid parameter.");
            }
            return getIndex(root, index + 1).k;
        }

        // 通过索引获取v
        public V getIndexValue(int index) {
            if (index < 0 || index >= this.size()) {
                throw new RuntimeException("invalid parameter.");
            }
            return getIndex(root, index + 1).v;
        }

        // 通过k获取v
        public V get(K key) {
            if (key == null) {
                throw new RuntimeException("invalid parameter.");
            }
            SBTNode<K, V> lastNode = findLastIndex(key);
            if (lastNode != null && key.compareTo(lastNode.k) == 0) {
                return lastNode.v;
            } else {
                return null;
            }
        }

        // 获取第一个key
        public K firstKey() {
            if (root == null) {
                return null;
            }
            SBTNode<K, V> cur = root;
            // 获取树的最左
            while (cur.l != null) {
                cur = cur.l;
            }
            return cur.k;
        }

        // 获取最后一个key
        public K lastKey() {
            if (root == null) {
                return null;
            }
            SBTNode<K, V> cur = root;
            // 获取树的最右
            while (cur.r != null) {
                cur = cur.r;
            }
            return cur.k;
        }

        // 返回小于或等于给定键的最大键,如果没有这样的键,则null
        public K floorKey(K key) {
            if (key == null) {
                throw new RuntimeException("invalid parameter.");
            }
            SBTNode<K, V> lastNoBigNode = findLastNoBigIndex(key);
            return lastNoBigNode == null ? null : lastNoBigNode.k;
        }

        // 返回大于或等于给定键的最小键,如果没有这样的键,则null
        public K ceilingKey(K key) {
            if (key == null) {
                throw new RuntimeException("invalid parameter.");
            }
            SBTNode<K, V> lastNoSmallNode = findLastNoSmallIndex(key);
            return lastNoSmallNode == null ? null : lastNoSmallNode.k;
        }


    }

三、SkipList跳表

1、介绍

map中进行大于小于操作,增删改查都是O(logn)

2、思路

3、代码

Last update:
Contributors: gaoqisen