Van emde boas树Java实现

简介

VEB树的基础理论在算法导论第三版第20章有详细介绍,这里不再赘述。

VEB树的优势

VEB树支持插入、删除、搜索、最大值、最小值、前驱、后继等操作。
这些操作的时间复杂度都是O(lglgu),u为关键字的全域。

接口定义


/**
 * Van emde boas树
 * 下面时间复杂度中 u表示树的容量
 *
 * @author: zyp
 * @since: 2022/1/18 下午5:46
 */
public interface IVEBTree {
    int size();

    /**
     * u: 可能存储的最大值
     * O(lglgu)
     */
    boolean contains(int value);

    /**
     * 最小值
     * O(1)
     */
    Integer minmum();

    /**
     * 最大值
     * O(1)
     */
    Integer maxmum();

    /**
     * 后继节点
     * O(lglgu)
     */
    Integer successor(int x);

    /**
     * 前驱节点
     * O(lglgu)
     */
    Integer predecessor(int x);

    /**
     * 插入
     * O(lglgu)
     */
    boolean insert(int value);

    /**
     * 删除
     * O(lglgu)
     */
    boolean delete(int value);

    void clear();
}

Java实现


import java.util.Arrays;

/**
 * ProtoVEB原型树
 *
 * @author: zyp
 * @since: 2022/1/18 下午5:46
 */
public class VEBTree implements IVEBTree {
    private ProtoVEBNode root;

    public VEBTree(int capacity) {
        if (capacity < 16) {
            capacity = 16;
        }
        root = new ProtoVEBNode(getOffset(capacity));
    }

    //    插入VEB树, 如果元素存在, 则忽略
    @Override
    public boolean insert(int value) {
        ensureCapacity(value + 1);
        return root.insert(value);
    }

    //    是否包含指定元素
    @Override
    public boolean contains(int value) {
        return root.contains(value);
    }

    @Override
    public int size() {
        return root == null ? 0 : root.size;
    }

    //    最小值
    @Override
    public Integer minmum() {
        if (root.size == 0) {
            return null;
        }
        return root.min;
    }

    //    最大值
    @Override
    public Integer maxmum() {
        if (root.size == 0) {
            return null;
        }
        return root.max;
    }

    //    x的后继节点
    @Override
    public Integer successor(int x) {
        if (root == null) {
            return null;
        }
        return root.successor(x);
    }

    //    x的前驱结点
    @Override
    public Integer predecessor(int x) {
        if (root == null) {
            return null;
        }
        return root.predecessor(x);
    }

    //    删除指定元素, 如果树种包含该元素, 则返回true
    @Override
    public boolean delete(int value) {
        if (value >= root.capacity) {
            return false;
        }
        return root.delete(value);
    }

    @Override
    public void clear() {
        root = new ProtoVEBNode(16);
    }


    //    扩展容量
    private void ensureCapacity(int capacity) {
        int offset = getOffset(capacity);
        while (root.offset < offset) {
            ProtoVEBNode newRoot = new ProtoVEBNode(root.offset << 1);
            newRoot.size = root.size;
            if (root.size > 0) {
                newRoot.min = root.min;
                newRoot.max = root.max;
                root.delete(root.min);
                newRoot.getOrCreateSummary().insert(0);
            }
            newRoot.getOrCreateClusters(0)[0] = root;
            root = newRoot;
        }
    }

    /**
     * 2^n < capacity <= 2^(n+1)
     * 计算n
     */
    private static int getOffset(int capacity) {
        if (capacity <= 2) {
            return 0;
        }
        if (capacity <= 4) {
            return 1;
        }
        int offset = 1;
        while (((1 << offset) << offset) < capacity) {
            offset <<= 1;
            if (offset == 16) {
                return offset;
            }
        }
        return offset;
    }

    private static class ProtoVEBNode {
        private int offset;
        private int capacity; // 容量 为2的整数次幂 capacity== (1<<offset)<<offset
        private ProtoVEBNode summary; // 统计clusters中的bit
        private ProtoVEBNode[] clusters; // clusters.length == 1<<offset
        private int min;
        private int max;
        private int size;

        private ProtoVEBNode() {
        }

        public ProtoVEBNode(int offset) {
            this.offset = offset;
            if (offset == 16) {
                this.capacity = Integer.MAX_VALUE;
            } else {
                this.capacity = (1 << offset) << offset;
            }
        }

        private ProtoVEBNode(int offset, int capacity) {
            this.offset = offset;
            this.capacity = capacity;
        }

        @Override
        public String toString() {
            return String.format("offset=%s,capacity=%s,size=%s,min=%s,max=%s", offset, capacity, size, min, max);
        }

        public Integer predecessor(int x) {
            if (offset == 0) {
                if (size == 0) {
                    return null;
                }
                if (min < x) {
                    return 0;
                }
                return null;
            }
            if (size == 0 || x <= min) {
                return null;
            }
            if (x > max) {
                return max;
            }
            if (size == 1) {
                return null;
            } else if (size == 2) {
                return min;
            }
            int high = high(x);
            ProtoVEBNode cluster = getCluster(high);
            if (cluster != null) {
                Integer predecessor = cluster.predecessor(low(x));
                if (predecessor != null) {
                    return index(high, predecessor);
                }
            }
            if (summary == null) {
                return min;
            }
            Integer predecessor = summary.predecessor(high);
            if (predecessor == null) {
                return min;
            }
            cluster = clusters[predecessor];
            if (cluster == null) {
                return min;
            }
            return index(predecessor, cluster.max);
        }

        public Integer successor(int x) {
            if (offset == 0) {
                if (size == 0) {
                    return null;
                }
                if (max > x) {
                    return 1;
                }
                return null;
            }
            if (size == 0 || x >= max) {
                return null;
            }
            if (x < min) {
                return min;
            }
            if (size == 1) {
                return null;
            } else if (size == 2) {
                return max;
            }
            int high = high(x);
            ProtoVEBNode cluster = clusters[high];
            if (cluster != null) {
                Integer successor = cluster.successor(low(x));
                if (successor != null) {
                    return index(high, successor);
                }
            }
            if (summary == null) {
                return max;
            }
            Integer successor = summary.successor(high);
            if (successor == null) {
                return max;
            }
            cluster = clusters[successor];
            if (cluster == null) {
                return max;
            }
            return index(successor, cluster.min);
        }

//        因子是2的整数次幂时, 可以用位运算快速计算除法/乘法/取余
        public int low(int value) {
//            value % (1 << offset)
            int segSize = 1 << offset;
            return value & (segSize - 1);
        }

        public int high(int value) {
//            value / (1 << offset)
            int segSize = 1 << offset;
            return (value & (-segSize)) >> offset;
        }

        public int index(int x, int y) {
//            x * (1<<offset) + y
            return (x << offset) + y;
        }

        public ProtoVEBNode getOrCreateSummary() {
            if (summary == null) {
                summary = createSubNode();
            }
            return summary;
        }

        private ProtoVEBNode createSubNode() {
            if (offset == 1) {
                return new ProtoVEBNode(0, 2);
            } else {
                return new ProtoVEBNode(offset >> 1);
            }
        }

        private ProtoVEBNode[] getOrCreateClusters(int capacity) {
            if (clusters == null) {
                clusters = new ProtoVEBNode[Math.max(16, capacity)];
            } else {
                if (clusters.length < capacity) {
//                    扩容
                    int len = clusters.length;
                    while (len < capacity) {
                        len <<= 1;
                    }
                    clusters = Arrays.copyOf(clusters, len);
                }
            }
            return clusters;
        }

        public ProtoVEBNode getCluster(int idx) {
            if (clusters == null || idx >= clusters.length) {
                return null;
            }
            return clusters[idx];
        }

        public ProtoVEBNode getOrCreateCluster(int idx) {
            ProtoVEBNode[] clusters = getOrCreateClusters(idx + 1);
            ProtoVEBNode cluster = clusters[idx];
            if (cluster == null) {
                cluster = createSubNode();
                clusters[idx] = cluster;
            }
            return cluster;
        }


        private boolean insertBase(int x) {
            if (size == 0) {
                min = max = x;
                size++;
                return true;
            } else if (size == 1) {
                if (min == x) {
                    return false;
                }
                if (x < min) {
                    min = x;
                } else {
                    max = x;
                }
                size++;
                return true;
            } else {
                return false;
            }
        }

        public boolean insert(int value) {
            if (offset == 0) {
                return insertBase(value);
            }
            if (size == 0) {
                min = max = value;
                size++;
                return true;
            } else {
                if (value == min || value == max) {
                    return false;
                }
                int insert;
                if (value < min) {
                    insert = min;
                    min = value;
                } else if (value > max) {
                    max = value;
                    insert = value;
                } else {
                    insert = value;
                }
                int high = high(insert);
                boolean ok = getOrCreateCluster(high).insert(low(insert));
                if (ok) {
                    getOrCreateSummary().insert(high);
                    size++;
                }
                return ok;
            }
        }

        private boolean deleteBase(int x) {
            if (size == 0) {
                return false;
            }
            if (size == 1) {
                if (min == x) {
                    size--;
                    return true;
                }
            } else {
                if (0 == x) {
                    min = max = 1;
                } else {
                    min = max = 0;
                }
                size--;
                return true;
            }
            return false;
        }

        public boolean delete(int value) {
            if (offset == 0) {
                return deleteBase(value);
            }
            if (size == 0 || value < min || value > max) {
                return false;
            }
            if (size == 1) {
                if (min == value) {
                    size--;
                    return true;
                } else {
                    return false;
                }
            }
            if (min == value) {
                int successor = successor(min);
                delete0(successor);
                min = successor;
                return true;
            } else if (max == value) {
                int predecessor = predecessor(max);
                delete0(value);
                max = predecessor;
                return true;
            } else {
                return delete0(value);
            }
        }

        private boolean delete0(int x) {
            int high = high(x);
            ProtoVEBNode cluster = clusters[high];
            boolean ok = cluster != null && cluster.delete(low(x));
            if (ok) {
                size--;
                if (cluster.size == 0) {
                    summary.delete(high);
                }
            }
            return ok;
        }

        public boolean contains(int value) {
            if (size == 0 || value < min || value > max) {
                return false;
            }
            if (min == value || max == value) {
                return true;
            }
            if (offset == 0) {
                return false;
            }
            if (size < 3) {
                return false;
            }
            int high = high(value);
            if (clusters != null && high < clusters.length) {
                ProtoVEBNode cluster = clusters[high];
                if (cluster != null) {
                    return cluster.contains(low(value));
                }
            }
            return false;
        }
    }
}

测试

分为正确性测试和性能测试

性能测试结果

TreeMap insert 15045 ms
ProtoVEB insert 11844 ms
TreeMap minimum 142 ms
ProtoVEB minimum 19 ms
TreeMap predecessor 14349 ms
ProtoVEB predecessor 7248 ms
TreeMap contains 13375 ms
ProtoVEB contains 2223 ms
TreeMap delete 14465 ms
ProtoVEB delete 6767 ms


import org.junit.Test;

import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author: zyp
 * @since: 2022/1/20 下午6:50
 */
public class SortedTreeTest {
    ThreadLocalRandom random = ThreadLocalRandom.current();

    //    VEB树正确性测试
    @Test
    public void testProtoVEB() {
        long start = System.currentTimeMillis();
        TreeHolder holder = new TreeHolder();
        int keyRange = 10000;
        int loopTimes = 10000000;
//        测试插入
        for (int i = 1; i <= loopTimes; i++) {
            int key = random.nextInt(keyRange);
            holder.insert(key);
            key = random.nextInt(keyRange);
            holder.delete(key);
        }
        long end = System.currentTimeMillis();
        System.out.println(String.format("cost %s ms", end - start));
    }

    //    VEB树性能测试
    @Test
    public void testProtoVEBTime() {
        long start;
        Integer zero = 0;
        int size = 10000000;
        int[] nums = new int[size];
        Integer[] numWrappers = new Integer[size];
        for (int i = 0; i < size; i++) {
            int v = random.nextInt(0, 100000000);
            nums[i] = v;
            numWrappers[i] = v;
        }
        TreeMap<Integer, Integer> map = new TreeMap<>();
//        ========================
        start = System.currentTimeMillis();
        for (Integer num : numWrappers) {
            map.put(num, zero);
        }
        System.out.println(String.format("TreeMap insert %s ms", System.currentTimeMillis() - start));
        VEBTree tree1 = new VEBTree(16);
        start = System.currentTimeMillis();
        for (int num : nums) {
            tree1.insert(num);
        }
        System.out.println(String.format("ProtoVEB insert %s ms", System.currentTimeMillis() - start));
//        ========================
        start = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            map.firstKey();
        }
        System.out.println(String.format("TreeMap minimum %s ms", System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            tree1.minmum();
        }
        System.out.println(String.format("ProtoVEB minimum %s ms", System.currentTimeMillis() - start));


//        ========================
        start = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            map.lowerKey(nums[i]);
        }
        System.out.println(String.format("TreeMap predecessor %s ms", System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            tree1.predecessor(nums[i]);
        }
        System.out.println(String.format("ProtoVEB predecessor %s ms", System.currentTimeMillis() - start));

//        ========================
        start = System.currentTimeMillis();
        for (Integer num : numWrappers) {
            map.containsKey(num);
        }
        System.out.println(String.format("TreeMap contains %s ms", System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        for (int num : nums) {
            tree1.contains(num);
        }
        System.out.println(String.format("ProtoVEB contains %s ms", System.currentTimeMillis() - start));
//        ========================
        start = System.currentTimeMillis();
        for (Integer num : numWrappers) {
            map.remove(num);
        }
        System.out.println(String.format("TreeMap delete %s ms", System.currentTimeMillis() - start));
        start = System.currentTimeMillis();
        for (int num : nums) {
            tree1.delete(num);
        }
        System.out.println(String.format("ProtoVEB delete %s ms", System.currentTimeMillis() - start));
//        ========================
    }


    private static class TreeHolder {
        private TreeMap<Integer, Integer> map = new TreeMap<>();
        private VEBTree tree1 = new VEBTree(16);
        private VEBTree tree2 = new VEBTree(16);

        public void delete(int key) {
            boolean remove = map.remove(key) != null;
            boolean delete;
            try {
                delete = tree1.delete(key);
            } catch (Throwable x) {
                tree2.delete(key);
                throw new RuntimeException(x);
            }
            if (!Objects.equals(remove, delete)) {
                tree2.delete(key);
                throw new RuntimeException("delete 错误");
            }
            try {
                if (tree1.contains(key)) {
                    tree2.delete(key);
                    throw new RuntimeException("delete 错误");
                }
            } catch (Throwable x) {
                tree2.delete(key);
                tree2.contains(key);
                throw new RuntimeException(x);
            }
            if (map.size() != tree1.size()) {
                tree2.delete(key);
                throw new RuntimeException("delete 错误");
            }

            tree2.delete(key);
        }

        public void insert(int key) {
            boolean ok = map.put(key, key) == null;
            boolean ok1;
            try {
                ok1 = tree1.insert(key);
            } catch (Throwable x) {
                tree2.insert(key);
                throw new RuntimeException(x);
            }
            if (!Objects.equals(ok, ok1)) {
                tree2.insert(key);
                throw new RuntimeException("insert 错误");
            }
            if (map.firstKey().intValue() != tree1.minmum().intValue()) {
                tree2.insert(key);
                tree2.minmum();
                throw new RuntimeException("minmum 错误");
            }
            if (map.lastKey().intValue() != tree1.maxmum().intValue()) {
                tree2.insert(key);
                tree2.maxmum();
                throw new RuntimeException("minmum 错误");
            }
            if (!tree1.contains(key)) {
                tree2.insert(key);
                tree2.contains(key);
                throw new RuntimeException("contains 错误");
            }
            Integer lowerKey = map.lowerKey(key);
            Integer predecessor;
            try {
                predecessor = tree1.predecessor(key);
            } catch (Throwable x) {
                tree2.insert(key);
                tree2.predecessor(key);
                throw new RuntimeException("predecessor 错误");
            }
            if (!Objects.equals(lowerKey, predecessor)) {
                tree2.insert(key);
                tree2.predecessor(key);
                throw new RuntimeException("predecessor 错误");
            }
            Integer higherKey = map.higherKey(key);
            Integer successor;
            try {
                successor = tree1.successor(key);
            } catch (Throwable x) {
                tree2.insert(key);
                tree2.successor(key);
                throw new RuntimeException("predecessor 错误");
            }
            if (!Objects.equals(higherKey, successor)) {
                tree2.insert(key);
                tree2.successor(key);
                throw new RuntimeException("successor 错误");
            }
            tree2.insert(key);
        }
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值