⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 btree.java

📁 B+树实现源码
💻 JAVA
字号:
package bplustree;

import java.util.*;
import com.xuedi.IO.*;
import com.xuedi.maths.*;

////// DisposeRoot ///////中的key参数有些问题

public class BTree {
  //用于记录每个节点中的键值数量
  public int keyAmount;
  //树的根节点
  public Node root;

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

  //在B树中插入叶节点/////////////////////////////////////////////////////////////
  public void insert(long key,Object pointer)
  {
    //找到应该插入的节点
    Node theNode = search(key,root);

    //在叶节点中找到空闲空间,有的话就把键放在那里
    if( !isFull(theNode) )
    {
      putKeyToNode(key,pointer,theNode);
    }else{
      //如果在适当的叶节点没有空间,就把该叶节点分裂成两个,并正确分配键值
      Node newNode = separateLeaf(key,pointer,theNode);
      //如果分裂的是根节点,就新建一个新的根节点将新建的节点作为他的字节点
      if( isRoot(theNode) )
      {
        DisposeRoot(theNode,newNode,newNode.keys[0]);
      }else{
        //将新建立的节点的指针插入到上层节点
        insertToInnerNode(theNode.parent,newNode,newNode.keys[0]);
      }
    }
  }
  //lowerNode是下级节点分离后新建立的那个节点///////////////////////////////////////
  //upperNode是lowerNode的上层节点
  private void insertToInnerNode(Node upperNode,Node lowerNode,long key)
  {
    //上层节点有空位就直接插入
    if( !isFull(upperNode) )
    {
      putKeyToNode(key,lowerNode,upperNode);
      //重置父节点指针
      pointerRedirect(upperNode);
      return;
    }else{
      //如果分裂的是根节点,就新建一个新的根节点将新建的节点作为他的子节点
      Node newNode;
      if( isRoot(upperNode) )
      {
        newNode = separateInnerNode(key,lowerNode,upperNode);
        Node newRoot = new Node(this.keyAmount);
        newRoot.pointer[0] = upperNode;
        newRoot.pointer[1] = newNode;
        upperNode.parent = newRoot;
        newNode.parent   = newRoot;
        newRoot.keyAmount = 1;
        newRoot.keys[0] = key;
        root = newRoot;
        //重置父节点指针
        pointerRedirect(upperNode);
        return;
      }else{
        //上层非根节点没有空位进行分裂和插入操作
        newNode = separateInnerNode(key,lowerNode,upperNode);
        //重置父节点指针
        pointerRedirect(upperNode);

        //记录要向上插入的键值在源节点中的位置(该键值在separateInnerNode()被保留在srcNode中)
        int keyToUpperNodePosition = upperNode.keyAmount;
        //向上递归插入
        insertToInnerNode(upperNode.parent,newNode,upperNode.keys[keyToUpperNodePosition]);
        //重置父节点指针
        pointerRedirect(newNode);
      }

    }
  }
  //将对应的内部节点进行分裂并正确分配键值,返回新建的节点
  private Node separateInnerNode(long key,Object pointer,Node srcNode)
  {
    Node newNode = new Node(this.keyAmount);
   //因为我在Node中预制了一个位置用于插入,而下面的函数(putKeyToLeaf())不进行越界检查
   //所以可以将键-指针对先插入到元节点,然后再分别放到两个节点中
   putKeyToNode(key,pointer,srcNode);

   //先前节点后来因该有(n+1)/2取上界个键-值针对
   int ptrSaveAmount = (int)com.xuedi.maths.NumericalBound.getBound(0,(double)(this.keyAmount+1)/2);
   int keySaveAmount = (int)com.xuedi.maths.NumericalBound.getBound(0,(double)(this.keyAmount)/2);
   int keyMoveAmount = (int)com.xuedi.maths.NumericalBound.getBound(1,(double)(this.keyAmount)/2);
   //(n+1)/2取上界个指针和n/2取上界个键留在源节点中
   //剩下的n+1)/2取下界个指n/2取下界个键留在源节点中
   for (int k = ptrSaveAmount; k < srcNode.keyAmount; k++) {
       newNode.add(srcNode.keys[k], srcNode.pointer[k]);
   }
   newNode.pointer[newNode.keyAmount] = srcNode.pointer[srcNode.pointer.length-1];
   srcNode.keyAmount = keySaveAmount;

   return newNode;
  }
  //将对应的叶节点进行分裂并正确分配键值,返回新建的节点///////////////////////////////
  private Node separateLeaf(long key,Object pointer,Node srcNode)
  {
    Node newNode = new Node(this.keyAmount);
    //兄弟间的指针传递
    newNode.pointer[this.keyAmount] = srcNode.pointer[this.keyAmount];

    //因为我在Node中预制了一个位置用于插入,而下面的函数(putKeyToLeaf())不进行越界检查
    //所以可以将键-指针对先插入到元节点,然后再分别放到两个节点中
    putKeyToNode(key,pointer,srcNode);

    //先前节点后来因该有(n+1)/2取上界个键-值针对
    int oldNodeSize = (int)com.xuedi.maths.NumericalBound.getBound(0,(double)(this.keyAmount+1)/2);
    for(int k = oldNodeSize; k <= this.keyAmount; k++)
    {
      newNode.add(srcNode.keys[k],srcNode.pointer[k]);
    }
    srcNode.keyAmount = oldNodeSize;

    //更改指针--让新节点成为就节点的右边的兄弟
    srcNode.pointer[this.keyAmount] = newNode;

    return newNode;
  }


  //把键值放到叶节点中--这个函数不进行越界检查////////////////////////////////////////
  private void putKeyToNode(long key,Object pointer,Node theNode)
  {
    int position = getInsertPosition(key,theNode);
    //进行搬迁动作--------叶节点的搬迁
    if( isLeaf(theNode) )
    {
        if(theNode.keyAmount <= position)
        {
           theNode.add(key,pointer);
           return;
        }
        else{
            for (int j = theNode.keyAmount - 1; j >= position; j--) {
              theNode.keys[j + 1] = theNode.keys[j];
              theNode.pointer[j + 1] = theNode.pointer[j];
            }
            theNode.keys[position] = key;
            theNode.pointer[position] = pointer;
        }
     }else{
          //内部节点的搬迁----有一定的插入策略:
          //指针的插入比数据的插入多出一位
          for (int j = theNode.keyAmount - 1; j >= position; j--) {
            theNode.keys[j + 1] = theNode.keys[j];
            theNode.pointer[j + 2] = theNode.pointer[j+1];
          }
          theNode.keys[position] = key;
          theNode.pointer[position+1] = pointer;
        }
    //键值数量加1
    theNode.keyAmount++;
  }

  //获得正确的插入位置
  private int getInsertPosition(long key,Node node)
  {
    //将数据插入到相应的位置
    int position = 0;
    for (int i = 0; i < node.keyAmount; i++) {
        if (node.keys[i] > key)
          break;
        position++;
    }
    return position;
  }
  //有用的辅助函数////////////////////////////////////////////////////////////////
  //判断某个结点是否已经装满了
 private boolean isFull(Node node)
 {
   if(node.keyAmount >= this.keyAmount)
     return true;
   else
     return false;
 }

 //判断某个节点是否是叶子结点
 private  boolean isLeaf(Node node)
 {
   //int i = 0;
   if(node.keyAmount == 0)
     return true;

   //如果向下的指针是Node型,则肯定不是叶子节点
   if(node.pointer[0] instanceof Node)
     return false;

   return true;
 }

 private boolean isRoot(Node node)
 {
   if( node.equals(this.root) )
     return true;
   return false;
 }

 //给内部节点中的自己点重新定向自己的父亲
  private void pointerRedirect(Node node)
  {
    for(int i = 0; i <= node.keyAmount; i++)
    {
      ((Node)node.pointer[i]).parent = node;
    }
  }

  //新建一个新的根节点将新建的节点作为他的字节点
  private void DisposeRoot(Node child1,Node child2,long key)
  {
        Node newRoot = new Node(this.keyAmount);
        newRoot.pointer[0] = child1;
        newRoot.pointer[1] = child2;
        newRoot.keyAmount = 1;
        newRoot.keys[0] = key;
        root = newRoot;
        //如果两个孩子是叶节点就让他们两个相连接
        if( isLeaf(child1) )
        {
          //兄弟间的指针传递
          child2.pointer[this.keyAmount] = child1.pointer[this.keyAmount];
          child1.pointer[this.keyAmount] = child2;
        }
        pointerRedirect(root);
        return;
  }
 ///////////////////////////////////////////////////////////////////////////////
  //用于寻找键值key所在的或key应该插入的节点
  //key为键值,curNode为当前节点--一般从root节点开始
  public Node search(long key,Node curNode)
  {
    if (isLeaf(curNode))
      return curNode;

    for (int i = 0; i < this.keyAmount; i++)
    {
        if (key < curNode.keys[i]) //判断是否是第一个值
          return search(key, (Node) curNode.pointer[i]);
        else if (key >= curNode.keys[i]) {
          if (i == curNode.keyAmount - 1) //如果后面没有值
          {
            //如果key比最后一个键值大,则给出最后一个指针进行递归查询
            return search(key,(Node) curNode.pointer[curNode.keyAmount]);
          }
          else {
            if (key < curNode.keys[i + 1])
              return search(key, (Node) curNode.pointer[i + 1]);
          }
        }
    }
    //永远也不会到达这里
    return null;
  }


}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -