B+树索引学生信息

引言:

  在数据库管理系统中,索引是一种重要的数据结构,用于加速对数据的访问。B+树是一种常用的索引结构,被广泛应用于数据库系统中,能够有效地支持精确查询,范围查询、排序和高效的插入,删除操作。

实现需求:

  1. 初始化 B+树,设置一些同学的信息(随机初始化 100 名同学(学号按顺序,其他信息随机),插入 B+树,使 B+树具有一定的高度)
  2. 修改 B+树中已存在同学的信息,更新树
  3. 向 B+树上添加一名新的同学信息,更新树的信息
  4. 删除 B+树上的一名同学的信息,更新树的信息
  5.  查询学号为 xxx 的学生信息,并输出
  6. 查询学号范围在为 10-18 之间的学生信息,并输出(输出顺序10-11-12-…

实现效果:

可以实现较大键值的插入。

范围查询:

ps:主要是参考了大佬的B+树实现

大佬原实现链接:【大数据存储】Java实现B+树(增、删、单值查询和范围查询)_java b+数据落盘-CSDN博客

代码:

BPlusNode.java:
package studentTree;

import java.util.*;

@SuppressWarnings("all")
public class BPlusNode<K extends Comparable<K>, V> {

    // 是否为叶子节点
    protected boolean isLeaf;

    //是否是根节点
    protected boolean isRoot;

    // 父节点
    protected BPlusNode<K, V> parent;

    // 叶节点的前驱节点
    protected BPlusNode<K, V> previous;

    // 叶节点的后继节点
    protected BPlusNode<K, V> next;

    // 节点的关键字列表
    protected List<Map.Entry<K, V>> entries;

    // 子节点列表
    protected List<BPlusNode<K, V>> children;

    //构造方法
    public BPlusNode(boolean isLeaf) {
        this.isLeaf = isLeaf;
        entries = new ArrayList();
        //如果不是叶子结点,则有孩子列表
        if (!isLeaf) {
            children = new ArrayList();
        }
    }

    //构造方法
    public BPlusNode(boolean isLeaf, boolean isRoot) {
        this(isLeaf);
        this.isRoot = isRoot;
    }

    //单值查找,找到key对应的value值
    public V query(K key) {
        //如果是叶子节点
        if (isLeaf) {
            //二分查找
            int low = 0, high = entries.size() - 1, mid;
            int comp;
            while (low <= high) {
                mid = (low + high) / 2;
                comp = entries.get(mid).getKey().compareTo(key);
                //等于key值
                if (comp == 0) {
                    //在entries列表中取value值
                    return entries.get(mid).getValue();
                } else if (comp < 0) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            //未找到所要查询的对象
            return null;
        }

        //如果不是叶子节点
        //如果key小于节点最左边的key,沿第一个子节点继续递归地搜索
        if (key.compareTo(entries.get(0).getKey()) < 0) {
            return children.get(0).query(key);
            //如果key大于等于节点最右边的key,沿最后一个子节点继续递归地搜索
        } else if (key.compareTo(entries.get(entries.size() - 1).getKey()) >= 0) {
            return children.get(children.size() - 1).query(key);
            //否则沿比key大的前一个子节点继续递归地搜索
        } else {
            //二分查找
            int low = 0, high = entries.size() - 1, mid = 0;
            int comp;
            while (low <= high) {
                mid = (low + high) / 2;
                comp = entries.get(mid).getKey().compareTo(key);
                if (comp == 0) {
                    return children.get(mid + 1).query(key);
                } else if (comp < 0) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            return children.get(low).query(key);
        }
    }


    public List<Map.Entry<K, V>> rangeQuery(K startInclude, K endExclude) {

        List<Map.Entry<K, V>> result = new ArrayList<>();
        rangeSearch(this, startInclude, endExclude, result);
        return result;
    }

    private void rangeSearch(BPlusNode<K, V> node, K startInclude, K endExclude, List<Map.Entry<K, V>> result) {
        if (node.isLeaf) {
            for (Map.Entry<K, V> entry : node.entries) {
                K key = entry.getKey();
                if (key.compareTo(startInclude) >= 0 && key.compareTo(endExclude) < 0) {
                    result.add(entry);
                }
            }
            BPlusNode<K, V> next = node.next;
            while (next != null) {
                for (Map.Entry<K, V> entry : next.entries) {
                    K key = entry.getKey();
                    if (key.compareTo(endExclude) < 0) {
                        result.add(entry);
                    } else {
                        return;
                    }
                }
                next = next.next;
            }
        } else {
            boolean foundRange = false;
            for (int i = 0; i < node.entries.size(); i++) {
                K key = node.entries.get(i).getKey();
                if (key.compareTo(startInclude) >= 0) {
                    rangeSearch(node.children.get(i), startInclude, endExclude, result);
                    foundRange = true;
                }
                if (foundRange || key.compareTo(endExclude) >= 0) {
                    break;
                }
            }
            if (!foundRange) {
                rangeSearch(node.children.get(node.children.size() - 1), startInclude, endExclude, result);
            }
        }
    }

    //二分查找找到key在entries中的位置
    protected int findIndex(K key) {
        int l = 0;
        int r = entries.size() - 1;
        int index = entries.size();
        while (l <= r) {
            int mid = l + ((r - l) >> 1);
            if (entries.get(mid).getKey().compareTo(key) >= 0) {
                index = mid;
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return index;
    }

    //插入 将key value键值对插入tree
    public void insertOrUpdate(K key, V value, BPlusTree<K, V> tree) {
        //如果是叶子节点
        if (isLeaf) {
            //小于树的度,不需要分裂,直接插入或更新
            if (contains(key) != -1 || entries.size() < tree.getOrder()) {
                insertOrUpdate(key, value);
                if (tree.getHeight() == 0) {
                    tree.setHeight(1);
                }
                return;
            }
            //需要分裂
            //分裂成左右两个节点
            BPlusNode<K, V> left = new BPlusNode<K, V>(true);
            BPlusNode<K, V> right = new BPlusNode<K, V>(true);
            //设置链接
            if (previous != null) {
                //前驱结点的next指针指向left
                previous.next = left;
                left.previous = previous;
            }
            if (next != null) {
                //后继结点的pre指针指向right
                next.previous = right;
                right.next = next;
            }
            //没有前驱
            if (previous == null) {
                tree.setHead(left);
            }
            //left的后继指针指向right
            left.next = right;
            //right的前驱指针指向left
            right.previous = left;
            //pre和next置空
            previous = null;
            next = null;

            //把原节点关键字复制到到分裂出来的两个新节点
            copy2Nodes(key, value, left, right, tree);

            //如果不是根节点
            if (parent != null) {
                //调整父子节点关系
                //在父节点的children序列中的位置
                int index = parent.children.indexOf(this);
                //移除当前结点
                parent.children.remove(this);
                //更新左右新结点的parent
                left.parent = parent;
                right.parent = parent;
                //在父节点的children序列中添加两个新结点
                parent.children.add(index, left);
                parent.children.add(index + 1, right);
                //在父节点的关键字列表的index位置中添加右结点的第一个关键字
                parent.entries.add(index, right.entries.get(0));
                entries = null; //删除当前节点的关键字信息
                children = null; //删除当前节点的孩子节点引用

                //父节点插入或更新关键字
                parent.updateInsert(tree);
                parent = null; //删除当前节点的父节点引用

                //如果是根节点
            } else {
                isRoot = false;
                //新建根节点
                BPlusNode<K, V> parent = new BPlusNode<K, V>(false, true);
                tree.setRoot(parent);
                //把相应的指针和序列更新
                left.parent = parent;
                right.parent = parent;
                parent.children.add(left);
                parent.children.add(right);
                parent.entries.add(right.entries.get(0));
                entries = null;
                children = null;
            }
            return;

        }
        //如果不是叶子节点
        //如果key小于等于节点最左边的key,沿第一个子节点继续递归地搜索
        if (key.compareTo(entries.get(0).getKey()) < 0) {
            children.get(0).insertOrUpdate(key, value, tree);
            //如果key大于节点最右边的key,沿最后一个子节点继续递归地搜索
        } else if (key.compareTo(entries.get(entries.size() - 1).getKey()) >= 0) {
            children.get(children.size() - 1).insertOrUpdate(key, value, tree);
            //否则沿比key大的前一个子节点继续递归地搜索
        } else {
            //二分查找
            int low = 0, high = entries.size() - 1, mid = 0;
            int comp;
            while (low <= high) {
                mid = (low + high) / 2;
                comp = entries.get(mid).getKey().compareTo(key);
                if (comp == 0) {
                    children.get(mid + 1).insertOrUpdate(key, value, tree);
                    break;
                } else if (comp < 0) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            if (low > high) {
                children.get(low).insertOrUpdate(key, value, tree);
            }
        }
    }

    //把原节点关键字复制到到分裂出来的两个新节点
    private void copy2Nodes(K key, V value, BPlusNode<K, V> left,
                            BPlusNode<K, V> right, BPlusTree<K, V> tree) {
        //左右两个节点关键字长度,比方说原结点有5个数,则left分2个,right分3个
        int leftSize = (tree.getOrder() + 1) / 2 + (tree.getOrder() + 1) % 2;
        boolean b = false;//用于记录新元素是否已经被插入
        for (int i = 0; i < entries.size(); i++) {
            //左结点
            if (leftSize != 0) {
                leftSize--;
                if (!b && entries.get(i).getKey().compareTo(key) > 0) {
                    //在左节点的entries序列中新建一个存放新元素
                    left.entries.add(new AbstractMap.SimpleEntry<K, V>(key, value));
                    b = true;
                    i--;
                } else {
                    left.entries.add(entries.get(i));
                }
                //右结点
            } else {
                if (!b && entries.get(i).getKey().compareTo(key) > 0) {
                    //在右节点的entries序列中新建一个存放新元素
                    right.entries.add(new AbstractMap.SimpleEntry<K, V>(key, value));
                    b = true;
                    i--;
                } else {
                    right.entries.add(entries.get(i));
                }
            }
        }
        //新元素仍没插入,说明新元素最大,把它放在右节点
        if (!b) {
            right.entries.add(new AbstractMap.SimpleEntry<K, V>(key, value));
        }
    }

    /**
     * 插入节点后中间节点的更新
     */
    protected void updateInsert(BPlusTree<K, V> tree) {

        //如果子节点数超出阶数,则需要分裂该节点
        if (children.size() > tree.getOrder()) {
            //分裂成左右两个节点
            BPlusNode<K, V> left = new BPlusNode<K, V>(false);
            BPlusNode<K, V> right = new BPlusNode<K, V>(false);
            //左右两个节点子节点的长度
            int leftSize = (tree.getOrder() + 1) / 2 + (tree.getOrder() + 1) % 2;
            int rightSize = (tree.getOrder() + 1) / 2;
            //复制子节点到分裂出来的新节点,并更新关键字
            for (int i = 0; i < leftSize; i++) {
                left.children.add(children.get(i));
                //把每个孩子的parent指针指向left
                children.get(i).parent = left;
            }
            for (int i = 0; i < rightSize; i++) {
                right.children.add(children.get(leftSize + i));
                //把每个孩子的parent指针指向right
                children.get(leftSize + i).parent = right;
            }
            for (int i = 0; i < leftSize - 1; i++) {
                //更新左节点的entries列表
                left.entries.add(entries.get(i));
            }
            for (int i = 0; i < rightSize - 1; i++) {
                //更新右节点的entries列表
                right.entries.add(entries.get(leftSize + i));
            }

            //如果不是根节点
            if (parent != null) {
                //调整父子节点关系
                int index = parent.children.indexOf(this);
                //在父节点的children列表中移除当前结点
                parent.children.remove(this);
                //更新指针指向
                left.parent = parent;
                right.parent = parent;
                //更新列表
                parent.children.add(index, left);
                parent.children.add(index + 1, right);
                parent.entries.add(index, entries.get(leftSize - 1));
                //原来的置空
                entries = null;
                children = null;

                //递归调用  父节点更新关键字
                parent.updateInsert(tree);
                parent = null;
                //如果是根节点
            } else {
                isRoot = false;
                //新建根节点
                BPlusNode<K, V> parent = new BPlusNode<K, V>(false, true);
                tree.setRoot(parent);
                tree.setHeight(tree.getHeight() + 1);
                //更新
                left.parent = parent;
                right.parent = parent;
                parent.children.add(left);
                parent.children.add(right);
                parent.entries.add(entries.get(leftSize - 1));
                //原来的置空
                entries = null;
                children = null;
            }
        }
    }

    /**
     * 删除节点后中间节点的更新
     */
    protected void updateRemove(BPlusTree<K, V> tree) {

        // 如果子节点数小于M / 2或者小于2,则需要合并节点
        if (children.size() < tree.getOrder() / 2 || children.size() < 2) {
            if (isRoot) {
                // 如果是根节点并且子节点数大于等于2,无需合并
                if (children.size() >= 2) return;
                // 否则与子节点合并
                //新建根节点
                BPlusNode<K, V> root = children.get(0);
                tree.setRoot(root);
                tree.setHeight(tree.getHeight() - 1);
                root.parent = null;
                root.isRoot = true;
                //原来的置空
                entries = null;
                children = null;
                return;
            }
            //计算前后节点
            //当前结点在父节点的children序列中的位置
            int currIdx = parent.children.indexOf(this);
            int prevIdx = currIdx - 1;
            int nextIdx = currIdx + 1;
            //新建前驱和后继
            BPlusNode<K, V> previous = null, next = null;
            if (prevIdx >= 0) {
                previous = parent.children.get(prevIdx);
            }
            if (nextIdx < parent.children.size()) {
                next = parent.children.get(nextIdx);
            }

            // 如果前驱节点子节点数大于M / 2并且大于2,则从其处借补
            if (previous != null
                    && previous.children.size() > tree.getOrder() / 2
                    && previous.children.size() > 2) {
                //前驱叶子节点末尾节点添加到首位
                int idx = previous.children.size() - 1;
                //新建借的结点
                BPlusNode<K, V> borrow = previous.children.get(idx);
                //借走了,从前驱结点的children序列中移除
                previous.children.remove(idx);
                //借过来了,更新相关
                borrow.parent = this;
                children.add(0, borrow);
                int preIndex = parent.children.indexOf(previous);

                entries.add(0, parent.entries.get(preIndex));
                parent.entries.set(preIndex, previous.entries.remove(idx - 1));
                return;
            }

            // 同理,如果后继节点子节点数大于M / 2并且大于2,则从其处借补
            if (next != null
                    && next.children.size() > tree.getOrder() / 2
                    && next.children.size() > 2) {
                //后叶子节点首位添加到末尾
                BPlusNode<K, V> borrow = next.children.get(0);
                next.children.remove(0);
                borrow.parent = this;
                children.add(borrow);
                int preIndex = parent.children.indexOf(this);
                entries.add(parent.entries.get(preIndex));
                parent.entries.set(preIndex, next.entries.remove(0));
                return;
            }

            // 如果前驱结点不够借,就同前驱节点合并
            if (previous != null
                    && (previous.children.size() <= tree.getOrder() / 2
                    || previous.children.size() <= 2)) {
                for (int i = 0; i < children.size(); i++) {
                    previous.children.add(children.get(i));
                }
                for (int i = 0; i < previous.children.size(); i++) {
                    previous.children.get(i).parent = this;
                }
                int indexPre = parent.children.indexOf(previous);
                previous.entries.add(parent.entries.get(indexPre));
                for (int i = 0; i < entries.size(); i++) {
                    previous.entries.add(entries.get(i));
                }
                children = previous.children;
                entries = previous.entries;

                //更新父节点的关键字列表
                parent.children.remove(previous);
                previous.parent = null;
                previous.children = null;
                previous.entries = null;
                //合并了,父节点的孩子就少一个
                parent.entries.remove(parent.children.indexOf(this));
                if ((!parent.isRoot
                        && (parent.children.size() >= tree.getOrder() / 2
                        && parent.children.size() >= 2))
                        || parent.isRoot && parent.children.size() >= 2) {
                    return;
                }
                //递归调用
                parent.updateRemove(tree);
                return;
            }

            // 同理,后继结点不够借,就同后面节点合并
            if (next != null
                    && (next.children.size() <= tree.getOrder() / 2
                    || next.children.size() <= 2)) {
                for (int i = 0; i < next.children.size(); i++) {
                    BPlusNode<K, V> child = next.children.get(i);
                    children.add(child);
                    child.parent = this;
                }
                int index = parent.children.indexOf(this);
                entries.add(parent.entries.get(index));
                for (int i = 0; i < next.entries.size(); i++) {
                    entries.add(next.entries.get(i));
                }
                parent.children.remove(next);
                next.parent = null;
                next.children = null;
                next.entries = null;
                parent.entries.remove(parent.children.indexOf(this));
                if ((!parent.isRoot && (parent.children.size() >= tree.getOrder() / 2
                        && parent.children.size() >= 2))
                        || parent.isRoot && parent.children.size() >= 2) {
                    return;
                }
                parent.updateRemove(tree);
                return;
            }
        }
    }

    //在B+树中删除key
    public V remove(K key, BPlusTree<K, V> tree) {
        //如果是叶子节点
        if (isLeaf) {
            //如果不包含该关键字,则直接返回
            if (contains(key) == -1) {
                return null;
            }
            //如果既是叶子节点又是根节点,直接删除
            if (isRoot) {
                if (entries.size() == 1) {
                    tree.setHeight(0);
                }
                return remove(key);
            }
            //如果关键字数大于M / 2,直接删除
            if (entries.size() > tree.getOrder() / 2 && entries.size() > 2) {
                return remove(key);
            }
            //如果自身关键字数小于M / 2,并且前驱节点关键字数大于M / 2,则从兄弟结点处借补
            if (previous != null &&
                    previous.parent == parent
                    && previous.entries.size() > tree.getOrder() / 2
                    && previous.entries.size() > 2) {
                //添加到首位
                int size = previous.entries.size();
                entries.add(0, previous.entries.remove(size - 1));
                int index = parent.children.indexOf(previous);
                parent.entries.set(index, entries.get(0));
                return remove(key);
            }
            //如果自身关键字数小于M / 2,并且后节点关键字数大于M / 2,则从右兄弟处借补
            if (next != null
                    && next.parent == parent
                    && next.entries.size() > tree.getOrder() / 2
                    && next.entries.size() > 2) {
                entries.add(next.entries.remove(0));
                int index = parent.children.indexOf(this);
                parent.entries.set(index, next.entries.get(0));
                return remove(key);
            }

            //前驱不够借,就同前面节点合并
            if (previous != null
                    && previous.parent == parent
                    && (previous.entries.size() <= tree.getOrder() / 2
                    || previous.entries.size() <= 2)) {
                V returnValue = remove(key);
                for (int i = 0; i < entries.size(); i++) {
                    //将当前节点的关键字添加到前节点的末尾
                    previous.entries.add(entries.get(i));
                }
                entries = previous.entries;
                parent.children.remove(previous);
                previous.parent = null;
                previous.entries = null;
                //更新链表
                if (previous.previous != null) {
                    BPlusNode<K, V> temp = previous;
                    temp.previous.next = this;
                    previous = temp.previous;
                    temp.previous = null;
                    temp.next = null;
                } else {
                    tree.setHead(this);
                    previous.next = null;
                    previous = null;
                }
                parent.entries.remove(parent.children.indexOf(this));
                if ((!parent.isRoot && (parent.children.size() >= tree.getOrder() / 2
                        && parent.children.size() >= 2))
                        || parent.isRoot && parent.children.size() >= 2) {
                    return returnValue;
                }
                //父节点要调整
                parent.updateRemove(tree);
                return returnValue;
            }
            //后继结点不够借,就同后面节点合并
            if (next != null
                    && next.parent == parent
                    && (next.entries.size() <= tree.getOrder() / 2
                    || next.entries.size() <= 2)) {
                V returnValue = remove(key);
                for (int i = 0; i < next.entries.size(); i++) {
                    //从首位开始添加到末尾
                    entries.add(next.entries.get(i));
                }
                next.parent = null;
                next.entries = null;
                parent.children.remove(next);
                //更新链表
                if (next.next != null) {
                    BPlusNode<K, V> temp = next;
                    temp.next.previous = this;
                    next = temp.next;
                    temp.previous = null;
                    temp.next = null;
                } else {
                    next.previous = null;
                    next = null;
                }
                //更新父节点的关键字列表
                parent.entries.remove(parent.children.indexOf(this));
                if ((!parent.isRoot && (parent.children.size() >= tree.getOrder() / 2
                        && parent.children.size() >= 2))
                        || parent.isRoot && parent.children.size() >= 2) {
                    return returnValue;
                }
                parent.updateRemove(tree);
                return returnValue;
            }
        }
        /*如果不是叶子节点*/

        //如果key小于等于节点最左边的key,沿第一个子节点继续搜索
        if (key.compareTo(entries.get(0).getKey()) < 0) {
            return children.get(0).remove(key, tree);
            //如果key大于节点最右边的key,沿最后一个子节点继续搜索
        } else if (key.compareTo(entries.get(entries.size() - 1).getKey()) >= 0) {
            return children.get(children.size() - 1).remove(key, tree);
            //否则沿比key大的前一个子节点继续搜索
        } else {
            //二分查找
            int low = 0, high = entries.size() - 1, mid = 0;
            int comp;
            while (low <= high) {
                mid = (low + high) / 2;
                comp = entries.get(mid).getKey().compareTo(key);
                if (comp == 0) {
                    return children.get(mid + 1).remove(key, tree);
                } else if (comp < 0) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            return children.get(low).remove(key, tree);
        }
    }

    // 判断当前节点是否包含该关键字
    protected int contains(K key) {
        int low = 0, high = entries.size() - 1, mid;
        int comp;
        while (low <= high) {
            mid = (low + high) / 2;
            comp = entries.get(mid).getKey().compareTo(key);
            if (comp == 0) {
                return mid;
            } else if (comp < 0) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }

    // 直接插入:插入到当前节点的关键字中
    protected void insertOrUpdate(K key, V value) {
        //二叉查找,插入
        int low = 0, high = entries.size() - 1, mid;
        int comp;
        while (low <= high) {
            mid = (low + high) / 2;
            comp = entries.get(mid).getKey().compareTo(key);
            if (comp == 0) {
                entries.get(mid).setValue(value);
                break;
            } else if (comp < 0) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        if (low > high) {
            entries.add(low, new AbstractMap.SimpleEntry<K, V>(key, value));
        }
    }

    // 直接删除:返回待删除节点的value值
    protected V remove(K key) {
        int low = 0, high = entries.size() - 1, mid;
        int comp;
        while (low <= high) {
            mid = (low + high) / 2;
            comp = entries.get(mid).getKey().compareTo(key);
            if (comp == 0) {
                return entries.remove(mid).getValue();
            } else if (comp < 0) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return null;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("isRoot: ");
        sb.append(isRoot);
        sb.append(", ");
        sb.append("isLeaf: ");
        sb.append(isLeaf);
        sb.append(", ");
        sb.append("keys: ");
        for (Map.Entry<K, V> entry : entries) {
            sb.append(entry.getKey());
            sb.append(", ");
        }
        sb.append(", ");
        return sb.toString();
    }

    //打印信息
    public void printBPlusTree(int index) {
        if (this.isLeaf) {
            System.out.print("层级:" + index + ",叶子节点,keys为: ");
            for (int i = 0; i < entries.size(); ++i)
                System.out.print(entries.get(i) + " ");
            System.out.println();
        } else {
            System.out.print("层级:" + index + ",非叶子节点,keys为: ");
            for (int i = 0; i < entries.size(); ++i)
                System.out.print(entries.get(i) + " ");
            System.out.println();
            for (int i = 0; i < children.size(); ++i)
                children.get(i).printBPlusTree(index + 1);
        }
    }
}

BPlusTree.java:
package studentTree;

/**
 * B+树的定义:
 * 1.任意非叶子结点最多有M个子节点;且M>2;M为B+树的阶数
 * 2.除根结点以外的非叶子结点至少有 (M+1)/2个子节点;
 * 3.根结点至少有2个子节点;
 * 4.除根节点外每个结点存放至少(M-1)/2和至多M-1个关键字;(至少1个关键字)
 * 5.非叶子结点的子树指针比关键字多1个;
 * 6.非叶子节点的所有key按升序存放,假设节点的关键字分别为K[0], K[1] … K[M-2],指向子女的指针分别为P[0], P[1]…P[M-1]。则有:
 *    P[0] < K[0] <= P[1] < K[1] …..< K[M-2] <= P[M-1]
 * 7.所有叶子结点位于同一层;
 * 8.为所有叶子结点增加一个链指针;
 * 9.所有关键字都在叶子结点出现
 */

@SuppressWarnings("all")
public class BPlusTree<K extends Comparable<K>, V> {

    // 根节点
    protected BPlusNode<K, V> root;

    // 树的阶数
    protected int order;

    // 叶子节点的链表头
    protected BPlusNode<K, V> head;

    // 树高
    protected int height = 0;

    public BPlusNode<K, V> getHead() {
        return head;
    }

    public void setHead(BPlusNode<K, V> head) {
        this.head = head;
    }

    public BPlusNode<K, V> getRoot() {
        return root;
    }

    public void setRoot(BPlusNode<K, V> root) {
        this.root = root;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getHeight() {
        return height;
    }

    public V query(K key) {return root.query(key);}

    public V remove(K key) {
        return root.remove(key, this);
    }

    public void insertOrUpdate(K key, V value) {
        root.insertOrUpdate(key, value, this);
    }

    public BPlusTree(int order) {
        if (order < 3) {
            System.out.print("度数要大于2!");
            System.exit(0);
        }
        this.order = order;
        root = new BPlusNode<K, V>(true, true);
        head = root;
    }

    public void printBPlusTree() {
        this.root.printBPlusTree(0);
    }
}

Student.java:
package studentTree;

public class Student {
    private int id;
    private String name;
    private String gender;
    private int score;

    public Student(int id, String name, String gender, int score) {
        this.id = id;
        this.name = name;
        this.gender = gender;
        this.score = score;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getGender() {
        return gender;
    }

    public int getScore() {
        return score;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setScore(int score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "ID: " + id + ", Name: " + name + ", Gender: " + gender + ", Score: " + score;
    }
}
BPlusTreeTest.java:
package studentTree;

import java.util.*;

public class BPlusTreeTest {

    private static final Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        int size = 100;
        int order = 4;
        BPlusTree<Integer, Student> tree = new BPlusTree<>(order);

        while (true) {
            System.out.println("请选择操作:");
            System.out.println("1. 初始化100个学生信息,并显示学生信息");
            System.out.println("2. 增加学生");
            System.out.println("3. 修改学生");
            System.out.println("4. 查询学生");
            System.out.println("5. 删除学生");
            System.out.println("6. 范围查询");
            System.out.println("7. 退出");

            int choice = scanner.nextInt();
            scanner.nextLine(); // consume newline

            switch (choice) {
                case 1:
                    initializeTree(tree, size);
                    //printAllStudents(tree);
                    break;
                case 2:
                    addStudent(tree);
                    break;
                case 3:
                    modifyStudent(tree);
                    break;
                case 4:
                    queryStudent(tree);
                    break;
                case 5:
                    deleteStudent(tree);
                    break;
                case 6:
                    rangeQueryStudents(tree);
                    break;
                case 7:
                    System.out.println("退出程序。");
                    return;
                default:
                    System.out.println("无效的选择,请重新选择。");
            }
        }
    }

    // 初始化 B+ 树,设置一些同学的信息
    private static void initializeTree(BPlusTree<Integer, Student> tree, int size) {
        Random random = new Random();
        for (int i = 1; i <= size; i++) {
            String name = "Student" + i;
            String gender = random.nextBoolean() ? "男" : "女";
            int score = random.nextInt(101);
            Student student = new Student(i, name, gender, score);
            System.out.println(student.toString());
            tree.insertOrUpdate(i, student);
        }
        System.out.println("初始化完成。");
    }

    // 打印所有学生信息
    private static void printAllStudents(BPlusTree<Integer, Student> tree) {
        List<Map.Entry<Integer, Student>> students = tree.getRoot().rangeQuery(1, 101);
        for (Map.Entry<Integer, Student> entry : students) {
            System.out.println(entry.getValue());
        }
    }

    // 增加学生
    private static void addStudent(BPlusTree<Integer, Student> tree) {
        System.out.print("请输入学生ID: ");
        int id = scanner.nextInt();
        scanner.nextLine(); // consume newline
        System.out.print("请输入学生姓名: ");
        String name = scanner.nextLine();
        System.out.print("请输入学生性别: ");
        String gender = scanner.nextLine();
        System.out.print("请输入学生成绩: ");
        int score = scanner.nextInt();
        scanner.nextLine(); // consume newline

        Student student = new Student(id, name, gender, score);
        tree.insertOrUpdate(id, student);
        System.out.println("添加学生成功。");
    }

    // 修改学生
    private static void modifyStudent(BPlusTree<Integer, Student> tree) {
        System.out.print("请输入要修改的学生ID: ");
        int oldId = scanner.nextInt();
        scanner.nextLine(); // consume newline

        Student student = tree.query(oldId);
        if (student != null) {
            System.out.print("请输入新的学生姓名: ");
            String name = scanner.nextLine();
            System.out.print("请输入新的学生性别: ");
            String gender = scanner.nextLine();
            System.out.print("请输入新的学生成绩: ");
            int score = scanner.nextInt();
            scanner.nextLine(); // consume newline

            System.out.print("是否需要修改学生ID? (y/n): ");
            String modifyIdChoice = scanner.nextLine();
            int newId = oldId;
            if (modifyIdChoice.equalsIgnoreCase("y")) {
                System.out.print("请输入新的学生ID: ");
                newId = scanner.nextInt();
                scanner.nextLine(); // consume newline
            }

            // 删除旧的学生信息并插入新的学生信息
            tree.remove(oldId);
            Student updatedStudent = new Student(newId, name, gender, score);
            tree.insertOrUpdate(newId, updatedStudent);

            System.out.println("修改学生成功: " + updatedStudent);
        } else {
            System.out.println("学生ID不存在。");
        }
    }

    // 查询学生
    private static void queryStudent(BPlusTree<Integer, Student> tree) {
        System.out.print("请输入要查询的学生ID: ");
        int id = scanner.nextInt();
        scanner.nextLine(); // consume newline

        Student student = tree.query(id);
        if (student != null) {
            System.out.println("学生信息: " + student);
        } else {
            System.out.println("学生ID不存在。");
        }
    }

    // 删除学生
    private static void deleteStudent(BPlusTree<Integer, Student> tree) {
        System.out.print("请输入要删除的学生ID: ");
        int id = scanner.nextInt();
        scanner.nextLine(); // consume newline

        Student student = tree.remove(id);
        if (student != null) {
            System.out.println("删除学生成功: " + student);
        } else {
            System.out.println("学生ID不存在。");
        }
    }

    private static void rangeQueryStudents(BPlusTree<Integer, Student> tree) {
        System.out.print("请输入范围查询的起始学号: ");
        int start = scanner.nextInt();
        System.out.print("请输入范围查询的结束学号: ");
        int end = scanner.nextInt();
        scanner.nextLine(); // consume newline

        List<Map.Entry<Integer, Student>> students = tree.getRoot().rangeQuery(start, end + 1);
        System.out.println("Students with ID in range " + start + "-" + end + ":");
        for (Map.Entry<Integer, Student> entry : students) {
            System.out.println(entry.getValue());
        }
    }
}

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值