B树(B-Tree)

时间:2024-04-29 16:55:06

        B树(B-Tree)是一种多路搜索树,用于在磁盘上存储和查找数据。它具有以下特点: 1. 每个节点可以包含多个子节点,通常用于处理大量数据和磁盘存储。 2. 每个节点中的键值按照升序排列,子节点的范围也由这些键值决定。 3. B树的节点中至少包含 t-1 个键值(t 为树的最小度数),最多包含 2t-1 个键值。 4. 所有叶子节点都在同一层,且不包含数据,用于存储实际数据的节点为内部节点。

import java.util.ArrayList;
import java.util.List;

public class BTree {

    private Node root;
    private int t; // 最小度数

    private static class Node {
        List<Integer> keys;
        List<Node> children;
        boolean leaf;

        public Node() {
            keys = new ArrayList<>();
            children = new ArrayList<>();
            leaf = true;
        }
    }

    public BTree(int t) {
        this.t = t;
        root = new Node();
    }

    // 插入操作
    public void insert(int key) {
        Node r = root;
        if (r.keys.size() == 2*t - 1) {
            Node s = new Node();
            root = s;
            s.children.add(r);
            splitChild(s, 0);
            insertNonFull(s, key);
        } else {
            insertNonFull(r, key);
        }
    }

    private void insertNonFull(Node x, int key) {
        int i = x.keys.size() - 1;
        if (x.leaf) {
            x.keys.add(0);
            while (i >= 0 && key < x.keys.get(i)) {
                x.keys.set(i+1, x.keys.get(i));
                i--;
            }
            x.keys.set(i+1, key);
        } else {
            while (i >= 0 && key < x.keys.get(i)) {
                i--;
            }
            i++;
            if (x.children.get(i).keys.size() == 2*t - 1) {
                splitChild(x, i);
                if (key > x.keys.get(i)) {
                    i++;
                }
            }
            insertNonFull(x.children.get(i), key);
        }
    }

    private void splitChild(Node x, int i) {
        Node z = new Node();
        Node y = x.children.get(i);
        z.leaf = y.leaf;
        for (int j = 0; j < t - 1; j++) {
            z.keys.add(y.keys.get(j + t));
        }
        if (!y.leaf) {
            for (int j = 0; j < t; j++) {
                z.children.add(y.children.get(j + t));
            }
        }
        for (int j = x.keys.size(); j > i; j--) {
            x.children.add(j + 1, x.children.get(j));
        }
        x.children.add(i + 1, z);
        for (int j = x.keys.size()-1; j >= i; j--) {
            x.keys.add(j + 1, x.keys.get(j));
        }
        x.keys.add(i, y.keys.get(t - 1));
        y.keys.subList(t - 1, y.keys.size()).clear();
        if (!y.leaf) {
            y.children.subList(t, y.children.size()).clear();
        }
    }

    // 查询操作
    public boolean search(int key) {
        return searchNode(root, key);
    }

    private boolean searchNode(Node x, int key) {
        int i = 0;
        while (i < x.keys.size() && key > x.keys.get(i)) {
            i++;
        }
        if (i < x.keys.size() && key == x.keys.get(i)) {
            return true;
        }
        if (x.leaf) {
            return false;
        } else {
            return searchNode(x.children.get(i), key);
        }
    }

    // 修改操作
    public void modify(int oldKey, int newKey) {
        delete(oldKey);
        insert(newKey);
    }

    // 删除操作
    public void delete(int key) {
        deleteKey(root, key);
        if (root.keys.isEmpty() && !root.leaf) {
            root = root.children.get(0);
        }
    }

    private void deleteKey(Node x, int key) {
        int idx = x.keys.indexOf(key);
        if (idx != -1) {
            if (x.leaf) {
                x.keys.remove(idx);
            } else {
                Node pred = x.children.get(idx);
                Node succ = x.children.get(idx + 1);
                if (pred.keys.size() >= t) {
                    int newKey = pred.keys.get(pred.keys.size() - 1);
                    deleteKey(pred, newKey);
                    x.keys.set(idx, newKey);
                } else if (succ.keys.size() >= t) {
                    int newKey = succ.keys.get(0);
                    deleteKey(succ, newKey);
                    x.keys.set(idx, newKey);
                } else {
                    pred.keys.add(key);
                    pred.keys.addAll(succ.keys);
                    pred.children.addAll(succ.children);
                    x.keys.remove(idx);
                    x.children.remove(idx + 1);
                    deleteKey(pred, key);
                }
            }
        } else {
            int i = 0;
            while (i < x.keys.size() && key > x.keys.get(i)) {
                i++;
            }
            Node child = x.children.get(i);
            if (child.keys.size() == t - 1) {
                Node leftSibling = (i > 0) ? x.children.get(i - 1) : null;
                Node rightSibling = (i < x.children.size() - 1) ? x.children.get(i + 1) : null;
                if (leftSibling != null && leftSibling.keys.size() >= t) {
                    child.keys.add(0, x.keys.get(i - 1));
                    x.keys.set(i - 1, leftSibling.keys.remove(leftSibling.keys.size() - 1));
                    if (!leftSibling.leaf) {
                        child.children.add(0, leftSibling.children.remove(leftSibling.children.size() - 1));
                    }
                } else if (rightSibling != null && rightSibling.keys.size() >= t) {
                    child.keys.add(x.keys.get(i));
                    x.keys.set(i, rightSibling.keys.remove(0));
                    if (!rightSibling.leaf) {
                        child.children.add(rightSibling.children.remove(0));
                    }
                } else {
                    if (leftSibling != null) {
                        leftSibling.keys.add(x.keys.remove(i - 1));
                        leftSibling.keys.addAll(child.keys);
                        leftSibling.children.addAll(child.children);
                        x.children.remove(i);
                    } else {
                        child.keys.addAll(rightSibling.keys);
                        child.children.addAll(rightSibling.children);
                        x.keys.remove(i);
                        x.children.remove(i + 1);
                    }
                    deleteKey(child, key);
                }
            } else {
                deleteKey(child, key);
            }
        }
    }

    // 中序遍历
    public void inorder() {
        inorderTraversal(root);
    }

    private void inorderTraversal(Node node) {
        if (node != null) {
            int i = 0;
            for (i = 0; i < node.keys.size(); i++) {
                if (!node.leaf) {
                    inorderTraversal(node.children.get(i));
                }
                System.out.print(node.keys.get(i) + " ");
            }
            if (!node.leaf) {
                inorderTraversal(node.children.get(i));
            }
        }
    }
}

        实战代码:

import java.util.ArrayList;
import java.util.List;

/**
 * BTree类
 * 
 * @author lingfeng
 *
 */
public class BTree {
    
    /**BTree 基础参数信息配置
    最小度数    t=2时,称作2-3-4数,表示只能存在2、3、4子女数**/
    private int t = 2;  
    
    /**非根节点最小关键字数**/
    private int minKeys = t-1;
    
    /**内节点最大关键字数**/
    private int maxKeys = 2*t - 1;
    
    /**树根节点**/
    private BTreeNode root;
    
    /**构造函数**/
    public BTree(){
        //初始化
        root = new BTreeNode();
        root.setLeaf(true);
    }
    /**构造函数  t 最小度数**/
    public BTree(int t){
        this();
        this.t  = t;
        minKeys = t - 1;
        maxKeys = 2*t - 1;
    }
    
    /**插入元素
     * 
     * 1.元素存在,插入元素
     * 2.元素不存在,插入元素
     * 
     * **/
    public void insertNode(Integer key){
        if(root.size() == maxKeys){   //根节点已满
            //进行根节点分割
            BTreeNode tmpNode = new BTreeNode();
            tmpNode.setLeaf(false);
            tmpNode.getChildrens().add(root);
            btreeSplitChild(tmpNode, 0, root);
            root = tmpNode;
        }
        insertRootNotFull(root, key);
    }
    
    public void insertRootNotFull(BTreeNode node, int key){
        
        //如果该节点为叶子节点
        if(node.isLeaf()){
            //寻找当前节点所有关键字
            ResultSearch result = divideSearch(node.getKeys(), key);
            //查询结果:成功  返回已经存在关键字位置
            if(result.result == true){
                
            }else{    //查询结果:失败   返回插入位置
                node.insertKey(key, result.index);
            }
            
        }else{
            //寻找当前节点所有关键字
            ResultSearch result = divideSearch(node.getKeys(), key);
            //查询结果:成功  返回已经存在关键字位置
            if(result.result == true){
                
            }else{    //查询结果:失败   返回插入子女节点的位置
                //判断子女状态
                BTreeNode childNode = node.childAt(result.index);
                if(node.childAt(result.index).size() == (2*t - 1)){
                    btreeSplitChild(node, result.index, childNode);
                    if(key > node.keyAt(result.index)){   //判断key落在 前半部分 或 后半部分
                        childNode = node.childAt(result.index + 1);
                    }
                }
                insertRootNotFull(childNode, key);
            }
        }
    }
    /**查询元素 (BTree查询)
     * 
     * 1.先查询节点的关键字列表
     * 2.失败则,查询子女节点;成功则,返回值
     * 3.递归搜索
     * 
     * **/
    public Integer search(BTreeNode node,Integer key){
        List<Integer> keys_tmp = node.getKeys();
        ResultSearch result = divideSearch(keys_tmp, key);
        if(result.result){
            return result.index;
        }else{
            return search(node.childAt(result.index), key);
        }
    }
    /**二分查询
     * 参数 元素列表、所需要查询元素的值
     * 
     * **/
    public ResultSearch divideSearch(List<Integer> elements, int key){
        
        int start = 0;    //扫描起始位置
        int end   = 0;    //扫描结束位置
        
        end = elements.size() - 1;
        
        int mid = 0; 
        while(start<=end){
            mid   = (start + end)/2;
            if(elements.get(mid) == key){ //满足等值条件
                break;
            }else if(elements.get(mid) > key){ //在列表前半部分
                //改变扫描结束位置
                end = mid - 1;
            }else if(elements.get(mid) <= key){
                //改变扫描开始位置
                start = mid + 1;
            }
        }
        
        boolean result = false;
        Integer index = 0;
        
        if(start<=end){  //二分查找成功
            result = true;
            index = mid;
        }else{    //查找失败,不存在元素
            result = false;
            index = start;
        }
        
        return new ResultSearch(result,index);
    }
    
    /**
     * 节点分割函数
     * @param parentNode  被分割节点的父节点
     * @param index  被分割节点在父节点的第index个子女索引位置
     * @param Node   被分割节点
     */
    public void btreeSplitChild(BTreeNode parentNode, int index, BTreeNode node){
        
        //新建一个节点,保存分割后[t+1 2t-1]数据
        BTreeNode subNode = new BTreeNode();
        //必须加上
        subNode.setLeaf(node.isLeaf());
        
        for(int i=1; i<=minKeys ; i++){
            subNode.getKeys().add(node.keyAt(t + i -1));
        }
        //保存分割点值[t]
        Integer key = node.keyAt(t - 1);
        //删除被分割节点的[t 2t-1]数据
        for(int i= maxKeys - 1; i>=minKeys; --i){
            node.getKeys().remove(i);
        }
        if(!node.isLeaf()){ //如果满节点不是叶节点,关键字分割后,需要将其子女进行操作
            
            //subNode应该拥有后半部分的子女
            for(int i=0 ; i<minKeys+1 ; ++i){
                subNode.getChildrens().add(node.childAt(i+t));
            }
            //并删除node后半部分的子女
            for(int i=maxKeys ; i>=minKeys+1; --i){
                node.getChildrens().remove(i);
            }
        }else{
            
        }
        //将[t]数据加入父节点中去
        parentNode.insertKey(key, index);
        //将新节点关联到父节点index+1子女中
        parentNode.insertChild(subNode, index+1);
    }
    public void delete(Integer key){
        delete(root, key);
    }
    
    public void delete(BTreeNode node, Integer key){
        //删除关键字时,必须保证关键字大于等于t
        assert node.size() >=t || node == root;
        
        //对当前节点进行二分查找
        ResultSearch resultSearch = divideSearch(node.getKeys(), key);
        
        //成功
        if(resultSearch.result){
            
            //如果当前节点属于叶子节点,可以直接进行删除
            if(node.isLeaf()){
                node.getKeys().remove(resultSearch.index.intValue());
            }else{
                //如果不是叶子节点 ,判断前于key子节点状态
                BTreeNode leftChildNode = node.childAt(resultSearch.index);
                if(leftChildNode.size() >= t){
                    
                    //从leftChildNode进行借值 代替当前需要删除的关键字
                    //删除当前节点关键字
                    node.getKeys().remove(resultSearch.index.intValue());
                    node.insertKey(leftChildNode.keyAt(leftChildNode.size()-1), resultSearch.index);
                    delete(leftChildNode, leftChildNode.keyAt(leftChildNode.size()-1));
                }else{
                    
                    BTreeNode rightChildNode = node.childAt(resultSearch.index + 1);
                    if(rightChildNode.size() >= t){
                        
                        //从rightChildNode进行借值 代替当前需要删除的关键字
                        node.getKeys().remove(resultSearch.index.intValue());
                        node.insertKey(rightChildNode.keyAt(0), resultSearch.index);
                        delete(rightChildNode, rightChildNode.keyAt(0));
                    }else{
                        
                        //对于索引的左右子节点的数量都等于t-1
                        //合适进行合并
                        //1.将父节点删除  将节点右子节点删除
                        node.getKeys().remove(resultSearch.index.intValue());
                        node.getChildrens().remove(resultSearch.index.intValue() + 1);
                        //2.将父节点添加到左子节点上
                        leftChildNode.getKeys().add(key);
                        //3.将删除的右子节点添加到左子节点上
                        for(int i=0 ; i<rightChildNode.size() ; i++){
                            leftChildNode.getKeys().add(rightChildNode.getKeys().get(i));
                        }
                        //如果右子节点非叶子节点,需要将其子女继承到左节点之下
                        if(!rightChildNode.isLeaf()){
                            for(int k=0 ; k<=rightChildNode.size() ; k++){
                                leftChildNode.getChildrens().add(rightChildNode.childAt(k));
                            }
                        }
                        //递归删除
                        delete(leftChildNode, key);
                        
                    }
                }
                
            }
            
        }else{ //失败
            if(node.isLeaf()){
                //不存在删除的对象
                System.out.println("不存在删除的对象");
                return ;
            }
            
            //获取子节点
            BTreeNode childNode = node.childAt(resultSearch.index);
            
            if(root == node && node.size()==0){
                root = childNode;
            }
            
            if(childNode.size() >= t){   //如果满足递归条件
                delete(childNode, key);
            }else{
                //不满足size == t
                //采取借关键字手段
                
                BTreeNode subNode = null;
                int subIndex = 0;
                //先检测右兄弟节点
                if(resultSearch.index < node.size()){
                    if(node.childAt(resultSearch.index+1).size() >=t){
                        subNode  = node.childAt(resultSearch.index+1);
                        subIndex = resultSearch.index + 1;
                    }
                }
                //测试左兄弟节点
                if(subNode == null){
                    if(resultSearch.index > 0){
                        if(node.childAt(resultSearch.index-1).size() >= t){
                            subNode  = node.childAt(resultSearch.index-1);
                            subIndex = resultSearch.index - 1;
                        }
                    }
                }
                //测试完成后
                if(subNode != null){  //存在兄弟节点大于等于t情况
                    //判断节点
                    if(subIndex > resultSearch.index){ //右兄弟
                        
                        //将右关键字插入自身
                        childNode.insertKey(node.keyAt(subIndex - 1), childNode.size());
                        node.getKeys().remove(subIndex - 1);
                        node.insertKey(subNode.keyAt(0), subIndex - 1);
                        subNode.getKeys().remove(0);
                        
                        //右兄弟非子叶节点,则带有孩子节点
                        if(!subNode.isLeaf()){
                            childNode.getChildrens().add(subNode.getChildrens().get(0));
                            subNode.getChildrens().remove(0);
                        }
                        
                    }else{  //左兄弟
                        
                        //将左关键字插入自身最前位置
                        childNode.insertKey(node.keyAt(subIndex), 0);
                        node.getKeys().remove(subIndex);
                        node.insertKey(subNode.keyAt(subNode.size()-1), subIndex);
                        subNode.getKeys().remove(subNode.size()-1);
                        
                        //如果左兄弟非子叶节点
                        if(!subNode.isLeaf()){
                            childNode.insertChild(subNode.childAt(subNode.size()), 0);
                            subNode.getChildrens().remove(subNode.size()-1);
                        }    
                    }
                    delete(childNode, key);
                    
                }else{
                    
                    //该节点的左右兄弟节点关键字都为t-1
                    //选择合并方案
                    if(resultSearch.index < node.size()){   //右兄弟存在
                        
                        subNode = node.childAt(resultSearch.index + 1);
                        
                        //childNode.getKeys().add(node.keyAt(resultSearch.index + 1));
                        childNode.getKeys().add(node.keyAt(resultSearch.index));
                        
                        node.getKeys().remove(resultSearch.index.intValue());
                        node.getChildrens().remove(resultSearch.index.intValue());
                        
                        for(int i=0 ; i<subNode.size() ; i++){
                            childNode.getKeys().add(subNode.keyAt(i));
                        }
                        
                        if(!subNode.isLeaf()){
                            for(int k=0 ; k<=subNode.size(); k++){
                                childNode.getChildrens().add(subNode.childAt(k));
                            }
                        }
                        
                    }else{      //左兄弟存在
                        
                        subNode = node.childAt(resultSearch.index - 1);
                        childNode.insertKey(node.keyAt(resultSearch.index-1), 0);
                        node.getKeys().remove(resultSearch.index - 1);
                        node.getChildrens().remove(resultSearch.index-1);
                        
                        for(int i=subNode.size()-1 ; i>=0 ; --i){
                            childNode.insertKey(subNode.keyAt(i), 0);
                        }
                        
                        if(!subNode.isLeaf()){
                            for(int k=subNode.size() ; k>=0 ; --k){
                                childNode.insertChild(subNode.childAt(k),0);
                            }
                        }
                        
                    }
                    if(root == node && node.size() == 0){
                        root = childNode;
                    }
                    delete(childNode, key);
                }
            }
            
        }
    }
    
}
class BTreeNode{
    
    /**当前节点keys列表**/
    private List<Integer> keys; 

    /**当前节点的child列表**/
    private List<BTreeNode> childrens;
    
    /**是否是叶子节点**/
    private boolean leaf;
    
    public BTreeNode(){
        keys      = new ArrayList<Integer>();
        childrens = new ArrayList<BTreeNode>();
        leaf = true;
    }
    /**
     * set and get methods
     * 
     * **/
    public List<Integer> getKeys() {
        return keys;
    }
    public void setKeys(List<Integer> keys) {
        this.keys = keys;
    }
    public List<BTreeNode> getChildrens() {
        return childrens;
    }
    public void setChildrens(List<BTreeNode> childrens) {
        this.childrens = childrens;
    }
    public boolean isLeaf() {
        return leaf;
    }
    public void setLeaf(boolean leaf) {
        this.leaf = leaf;
    }
    
    /**
     * 获取子女节点
     * @param index
     * @return
     */
    public BTreeNode childAt(int index){
        return childrens.get(index);
    }
    /**
     * 获取关键字
     * @param index
     * @return
     */
    public Integer keyAt(int index){
        return keys.get(index);
    }
    
    /**
     * 获取节点关键字数量
     * @return
     */
    public int size(){
        return keys.size();
    }
    
    /**
     * 插入key到指定的index中去
     * @param key
     * @param index
     */
    public void insertKey(Integer key, int index){
        //插入key到指定的索引位置
        //保存索引前的所有关键字
        List<Integer> newlist = new ArrayList<Integer>();
        
        for(int i=0; i<index ; i++){
            newlist.add(keys.get(i));
        }
        //插入关键字
        newlist.add(key);
        //保存索引后多关键字
        for(int i=index ; i<keys.size() ; i++){
            newlist.add(keys.get(i));
        }
        //将新的关键字集合设置为节点关键字集合对象
        keys = newlist;
    }
    
    /**
     * 插入node子女到指定的index位置
     * @param node
     * @param index
     */
    public void insertChild(BTreeNode node, int index){
        //插入child
        List<BTreeNode> newlist = new ArrayList<BTreeNode>();
        for(int i=0 ; i< index ; i++){
            newlist.add(childrens.get(i));
        }
        newlist.add(node);
        for(int i=index ; i<childrens.size() ; i++){
            newlist.add(childrens.get(i));
        }
        childrens = newlist;
    }
}
/**
 * 查询结果类
 * result 标识成功的状态 true or false
 * index 成功后返回元素的索引
 *       失败后返回元素的插入位置 
 * @author lingfeng
 */
class ResultSearch{
    
    public Integer index;
    public boolean result;
    
    public ResultSearch(boolean rs, Integer i){
        index  = i;
        result = rs;
    }
}