isLeaf 判断当前节点是否为叶节点
isRoot 判断当前节点是否为根结点
parent 当前节点的父结点
children 当前节点孩子结点集合
(其中只有非叶子有孩子结点)
keyword 当前节点关键字集合
(非叶子的关键字作为划分孩子节点的依据,当前节点关键字总个数比孩子结点个数少1)
如果当前节点是叶子节点,会与前后节点一起形成一个双向链表
pre为当前叶子节点的上一个节点
next为当前叶子节点的后一个结点
其余函数为getset方法
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.所有关键字都在叶子结点出现
root为B+树的根结点
maxorder为B+树节点的最大分支树,即某节点孩子结点的最大个数,比节点关键字个数大1
height为树高 初始树高为0
head 叶子节点形成的双向链表的头结点
(一)TestRandomsearch:/构造B+树/
TestRandomSearch:
调用insertNode将Map中的键值对存入B+树中,用于构造B+树,并输出构建树的时长.
insertNode:
调用add从根结点开始进行深搜
add:
在搜索时利用二分查找快速找到对应关键字/关键字划定出的孩子结点,并进行对应的插入和调整节点关系等操作
如果当前节点为叶节点,且当前关键字已存在或关键字个数小于maxorder:
直接调用insertNode
否则对当前叶节点进行分裂,分裂成left、right节点,并分配原有节点的关键字,继承原有的节点关系(父结点,在链表中的位置)
由于此时可能改变当前节点的父结点的节点个数导致当前节点的父结点的孩子结点个数可能大于maxorder,调用updateParent
如果当前节点为非叶子节点,则对当前keywords进行二分查找
如果key小于节点最左边的keywords,沿第一个孩子节点继续搜索;
如果key大于节点最右边的keywords,沿最后一个孩子节点继续搜索;
否则利用二分查找,找到比key大的前一个结点进行递归搜索。
①insertLeaf
对叶子节点的插入和更新(这里的更新为当发现当前key值存在(调用contains),不插入新键值对,而是对key所对应的value进行更新)
②updateParent
如果当前节点的父结点的孩子节点个数大于maxorder则将父节点进行分裂,将父节点关键字分配给其分裂后的left、right节点,调整left、继承父结点的节点关系(父结点和子节点),并将父结点清空。
③contains
判断当前叶子节点是否包含key值,没有则返回-1,有则返回key值在list中的index
(二)TestRandomSearch/搜索B+树上节点/
如果当前节点是叶子节点,直接利用二分查找查找关键字
如果当前节点为非叶子节点,则对当前keywords进行二分查找
如果key小于节点最左边的keywords,沿第一个孩子节点继续搜索;
如果key大于节点最右边的keywords,沿最后一个孩子节点继续搜索;
否则利用二分查找,找到比key大的前一个结点进行递归搜索。
调用org.apache.poi.ss包对excel表格按行进行读取,将结果存入Map中
调用org.jb2011.inf.beautyeye进行界面美化。
界面槽函数设计,调用TestRandomSearch进行查找,并将查找结果在界面上显示
调用TestRandomsearch构造B+树,生成界面
package b_index;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
public class BplusNode <K extends Comparable<K>,V>{
private boolean isLeaf;//是否是叶节点
private boolean isRoot;//是否是根结点
private BplusNode<K,V> parent;//父结点
private List<BplusNode<K,V>> children;//子节点
private List<Entry<K,V>>keywords;//关键字
private BplusNode<K,V>pre;//叶节点的前结点
private BplusNode<K,V>next;//叶结点的后结点
/*构造函数*/
public BplusNode(boolean isLeaf) {
this.isLeaf = isLeaf;
keywords=new ArrayList<Entry<K, V>>();
if(!isLeaf){
children=new ArrayList<BplusNode<K, V>>();
}
}
public BplusNode(boolean isLeaf, boolean isRoot) {
this(isLeaf);
this.isRoot = isRoot;
}
/*GetSet方法*/
public boolean getLeaf() {
return isLeaf;
}
public void setLeaf(boolean leaf) {
this.isLeaf = leaf;
}
public boolean getRoot() {
return isRoot;
}
public void setRoot(boolean root) {
this.isRoot = root;
}
public BplusNode<K, V> getParent() {
return parent;
}
public void setParent(BplusNode<K, V> parent) {
this.parent = parent;
}
public List<BplusNode<K, V>> getChildren() {
return children;
}
public void setChildren(List<BplusNode<K, V>> children) {
this.children = children;
}
public List<Entry<K, V>> getKeywords() {
return keywords;
}
public void setKeywords(List<Entry<K, V>> keywords) {
this.keywords = keywords;
}
public BplusNode<K, V> getPre() {
return pre;
}
public void setPre(BplusNode<K, V> pre) {
this.pre = pre;
}
public BplusNode<K, V> getNext() {
return next;
}
public void setNext(BplusNode<K, V> next) {
this.next = next;
}
@Override
public String toString(){
return getClass().getName()+","
+"isLeaf:"+getLeaf()+","
+"isRoot"+getRoot()+","
+"keywords"+getKeywords();
}
}
package b_index;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map;
public class BplusTree< K extends Comparable<K>,V > {
private BplusNode<K,V>root;//根结点
private int maxorder;//阶数
private int height;//树高
private BplusNode<K,V>head;//叶子节点的链表头
public BplusTree(int maxorder) {
if(maxorder<3){
System.out.print("order must be greater than 2");
System.exit(0);
}
this.maxorder=maxorder;
this.root=new BplusNode<K, V>(true,true);
this.head=root;
}
public BplusNode<K, V> getRoot() {
return root;
}
public void setRoot(BplusNode<K, V> root) {
this.root = root;
}
public int getMaxorder() {
return maxorder;
}
public void setMaxorder(int maxorder) {
this.maxorder = maxorder;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public BplusNode<K, V> getHead() {
return head;
}
public void setHead(BplusNode<K, V> head) {
this.head = head;
}
/*增加一个节点*/
public void insertNode(K key,V value){
add(this.getRoot(),key,value);
}
protected void add(BplusNode<K,V>curNode,K key,V value){
int kwsize = curNode.getKeywords().size();
//1.如果是叶子节点
if(curNode.getLeaf()==true){
//1.1不需要分裂,直接插入或更新
if(contains(curNode,key)!=-1||kwsize<this.getMaxorder()){
//插入或更新
insertLeaf(curNode,key,value);
if(this.getHeight()==0){
this.setHeight(1);
}
//System.out.println("叶子");
return;
}
//1.2需要分裂
//1.2.1分裂成左右两个节点
//System.out.println("裂开!!!");
BplusNode<K,V>left=new BplusNode<K,V>(true);
BplusNode<K,V>right=new BplusNode<K,V>(true);
//设置链接
if(curNode.getPre()!=null){
curNode.getPre().setNext(left);
left.setPre(curNode.getPre());
}
if(curNode.getNext()!=null){
curNode.getNext().setPre(right);
right.setNext(curNode.getNext());
}
if(curNode.getPre()==null){
this.setHead(left);
}
left.setNext(right);
right.setPre(left);
curNode.setPre(null);
curNode.setNext(null);
//分配原节点关键字
int leftSize=(this.getMaxorder()+1)/2+(this.getMaxorder()+1)%2;
boolean flag=false;//用于记录新元素是否已经被插入
for(int i=0;i<kwsize;i++){
if(leftSize!=0){
leftSize--;
//插入新元素
if(!flag&&curNode.getKeywords().get(i).getKey().compareTo(key)>0){
left.getKeywords().add(new SimpleEntry<K, V>(key,value));
flag=true;
i--;
}else{
left.getKeywords().add(curNode.getKeywords().get(i));
}
}else{
if(!flag&&curNode.getKeywords().get(i).getKey().compareTo(key)>0){
right.getKeywords().add(new SimpleEntry<K, V>(key,value));
flag=true;
i--;
}else{
right.getKeywords().add(curNode.getKeywords().get(i));
}
}
}
if(!flag){
right.getKeywords().add(new SimpleEntry<K, V>(key,value));
}
//调整父子节点关系
BplusNode<K,V> curparent=curNode.getParent();
//1.如果不是根节点
if(curparent!=null){
int index=curparent.getChildren().indexOf(curNode);
curparent.getChildren().remove(curNode);
left.setParent(curparent);
right.setParent(curparent);
curparent.getChildren().add(index,left);
curparent.getChildren().add(index+1,right);
curparent.getKeywords().add(index,right.getKeywords().get(0));
//父结点插入或更新关键字
updateParent(curparent);
curNode.setParent(null);
//如果是根节点
}else{
curNode.setRoot(false);
BplusNode<K,V>parent=new BplusNode<K,V>(false,true);
this.setRoot(parent);
left.setParent(parent);
right.setParent(parent);
parent.getChildren().add(left);
parent.getChildren().add(right);
parent.getKeywords().add(right.getKeywords().get(0));
}
//删除当前节点信息
curNode.setKeywords(null);
curNode.setChildren(null);
//System.out.println("3");
return ;
}else {
//System.out.println("非叶子");
//2.如果不是叶子节点,递归查找
//2.1如果key小于等于节点最左边的key,沿第一个子节点继续搜索
if (key.compareTo(curNode.getKeywords().get(0).getKey()) < 0) {
add(curNode.getChildren().get(0), key, value);
//2.2如果key节点大于节点最右边的key,沿最后一个节点继续搜索
}else if(key.compareTo(curNode.getKeywords().get(kwsize-1).getKey())>=0){
add(curNode.getChildren().get(curNode.getChildren().size()-1),key,value);
//2.3否则沿比key大的前一个子节点继续搜索
}else{
int low=0,high=kwsize-1,mid=0;
int comp;
Map.Entry<K,V> curentry;
while(low<=high){
mid=(low+high)/2;
curentry=curNode.getKeywords().get(mid);
comp=curentry.getKey().compareTo(key);
if(comp==0){
add(curNode.getChildren().get(mid+1),key,value);
break;
}else if(comp<0){
low=mid+1;
}else{
high=mid-1;
}
}
if(low>high){
add(curNode.getChildren().get(low),key,value);
}
}
//System.out.println("4");
}
}
/*判断当前节点是否包含关键字*/
protected int contains(BplusNode<K,V>curNode,K key){
int low=0,high=curNode.getKeywords().size()-1,mid;
//System.out.println("当前节键值对个数: "+high);
int comp;
while(low<=high){
mid=(low+high)/2;
comp=curNode.getKeywords().get(mid).getKey().compareTo(key);
// System.out.println("comp= "+comp);
// System.out.println("mid= "+mid);
if(comp==0){
return mid;
}else if(comp<0){
low=mid+1;
}else{
high=mid-1;
}
}
//System.out.println("1");
return -1;
}
/*叶节点--插入到当前节点关键字中*/
protected void insertLeaf(BplusNode<K,V>curNode,K key,V value){
int low=0,high=curNode.getKeywords().size()-1,mid;
int comp;
while(low<=high){
mid=(low+high)/2;
comp=curNode.getKeywords().get(mid).getKey().compareTo(key);
if(comp==0){
//找到键值则更新其值
curNode.getKeywords().get(mid).setValue(value);
break;
}else if(comp<0){
low=mid+1;
}else{
high=mid-1;
}
}
//没找到则插入新值
if(low>high){
curNode.getKeywords().add(low,new SimpleEntry<K,V>(key,value));
//System.out.println("叶节点插入成功");
}
}
protected void updateParent(BplusNode<K,V> parent){
//如果子节点超出阶数,则需要分裂该结点
int childsize=parent.getChildren().size();
//System.out.println("检查父结点孩子节点个数为:"+parent.getChildren().size());
if(childsize>this.getMaxorder()){
//System.out.println("父结点裂开");
//分裂成左右两个子节点
BplusNode<K,V>left=new BplusNode<K,V>(false);
BplusNode<K,V>right=new BplusNode<K,V>(false);
//左右两个子节点的长度
int leftSize=(this.getMaxorder()+1)/2+(this.getMaxorder()+1)%2;
int rightSize=(this.getMaxorder()+1)/2;
//复制子节点到分裂出来的新节点,并更新关键字
for(int i=0;i<leftSize;i++){
left.getChildren().add(parent.getChildren().get(i));
parent.getChildren().get(i).setParent(left);
}
for(int i=0;i<rightSize;i++){
right.getChildren().add(parent.getChildren().get(leftSize+i));
parent.getChildren().get(leftSize+1).setParent(right);
}
for(int i=0;i<leftSize-1;i++){
left.getKeywords().add(parent.getKeywords().get(i));
}
for(int i=0;i<rightSize-1;i++){
right.getKeywords().add(parent.getKeywords().get(leftSize+i));
}
//如果不是根节点
BplusNode<K,V>grandParent=parent.getParent();
if(grandParent!=null){
//调整父子节点关系
int index=grandParent.getChildren().indexOf(parent);
grandParent.getChildren().remove(parent);
left.setParent(grandParent);
right.setParent(grandParent);
grandParent.getChildren().add(index,left);
grandParent.getChildren().add(index+1,right);
grandParent.getKeywords().add(index,parent.getKeywords().get(leftSize-1));
//父节点更新关键字
updateParent(grandParent);
parent.setParent(null);
}else{
parent.setRoot(false);
grandParent=new BplusNode<K,V>(false,true);
this.setRoot(grandParent);
this.setHeight(this.getHeight()+1);
// System.out.println("keyword:"+parent.getKeywords());
// System.out.println("\n\n\n\n\nheight++="+this.getHeight());
// System.out.println("parentsize:"+parent.getChildren().size());
left.setParent(grandParent);
right.setParent(grandParent);
grandParent.getChildren().add(left);
grandParent.getChildren().add(right);
grandParent.getKeywords().add(parent.getKeywords().get(leftSize-1));
}
parent.setChildren(null);
parent.setKeywords(null);
}
}
/*查询一个结点*/
public V searchNode(K key){
return get(root,key);
}
protected V get(BplusNode<K,V>curNode,K key){
//递归+二分查找
int kwsize=curNode.getKeywords().size();
//如果是叶子节点
if(curNode.getLeaf()==true){
int low=0,high=kwsize-1,mid;
Map.Entry<K,V> curentry;
int comp;
while(low<=high){
mid=(low+high)/2;
curentry=curNode.getKeywords().get(mid);
comp=curentry.getKey().compareTo(key);
if(comp==0){
return curentry.getValue();
}else if(comp<0){
low=mid+1;
}else{
high=mid-1;
}
}
//未找到所要查询的对象
return null;
}
/*如果不是叶子节点*/
//如果key小于节点最左边的key,沿第一个子节点继续搜索
if(key.compareTo(curNode.getKeywords().get(0).getKey())<0){
return get(curNode.getChildren().get(0),key);
//如果key大于等于当前节点最右边的key,沿最后一个子节点继续搜索
}else if (key.compareTo(curNode.getKeywords().get(kwsize-1).getKey())>=0){
return get(curNode.getChildren().get(curNode.getChildren().size()-1),key);
//否则沿比key大的前一个子节点继续搜索
}else{
int low=0,high=kwsize-1,mid=0;
Map.Entry<K,V> curentry;
int comp;
while(low<=high){
mid=(low+high)/2;
curentry=curNode.getKeywords().get(mid);
comp=curentry.getKey().compareTo(key);
if(comp==0){
return get(curNode.getChildren().get(mid+1),key);
}else if(comp<0){
low=mid+1;
}else{
high=mid-1;
}
}
return get(curNode.getChildren().get(low),key);
}
}
public static String[] testRandomSearch(BplusTree<String,String>tree,String key){
System.out.println("Begin random search...");
long current=System.currentTimeMillis();
String value[]=new String [2];
if(tree.searchNode(key)==null){
return null;
}else{
value[0]=tree.searchNode(key);
//System.out.println("Search"+key+":"+value);
}
long duration=System.currentTimeMillis()-current;
System.out.println("time elpsed for duration for bplustree: "+duration);
value[1]=String.valueOf(duration);
return value;
}
public static BplusTree<String,String>testRandomInsert(Map<String,String>map,int size,int order){
BplusTree<String,String>tree=new BplusTree<String,String>(order);
System.out.println("\nTest random insert "+size+" datas.of order: "+order);
long current =System.currentTimeMillis();
int num=0;
for(String key:map.keySet()){
tree.insertNode(key,map.get(key));
num++;
//System.out.println("InsertNode!:"+num);
}
long duration =System.currentTimeMillis()-current;
System.out.println("time elpsed for duration for bplustree_construct: "+duration);
System.out.println(tree.getHeight());
return tree;
}
}
package index_file;
import b_index.BplusTree;
import binary_search.Binary;
import utils.ReadFromXlsx;
import indexView.IndexView;
import javax.swing.*;
import java.io.FileNotFoundException;
import java.util.Map;
public class App {
public static void main(String[] args)throws Exception
{
int m=100;
Map<String,String>map= ReadFromXlsx.Load();
System.out.println("size: "+map.size());
构造树,生成界面进行查找
BplusTree<String,String>tree= BplusTree.testRandomInsert(map,map.size(),m);
System.out.println("Tree Height: "+tree.getHeight());
//界面
IndexView dialog=new IndexView(tree);
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
}
}
package indexView;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import b_index.BplusTree;
public class IndexView extends JDialog {
/**
*
*/
private static final long serialVersionUID = 1L;
private final JPanel contentPanel = new JPanel();
private JTextField indexkey;
private JTextField desc_text;
private JTextField Sc_text;
public BplusTree<String,String> tree ;
private JTextField time_text;
/**
* Create the dialog.
* @throws Exception
*/
public IndexView(final BplusTree<String,String> tree) {
try
{
org.jb2011.lnf.beautyeye.BeautyEyeLNFHelper.launchBeautyEyeLNF();
}
catch(Exception e)
{
System.out.println("加载失败!");
}
this.tree = tree;
setTitle("Index File");
setBounds(100, 100, 523, 365);
getContentPane().setLayout(new BorderLayout());
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
getContentPane().add(contentPanel, BorderLayout.CENTER);
contentPanel.setLayout(null);
JLabel lblNewLabel = new JLabel("关键字:");
lblNewLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16));
lblNewLabel.setBounds(33, 41, 64, 36);
contentPanel.add(lblNewLabel);
indexkey = new JTextField();
indexkey.setBounds(97, 46, 255, 30);
contentPanel.add(indexkey);
indexkey.setColumns(10);
JButton search_btn = new JButton("查找");
search_btn.setFont(new Font("微软雅黑", Font.PLAIN, 16));
search_btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String key = indexkey.getText().trim();
if(key==""||key.equals("")) {
JOptionPane.showMessageDialog(null, "请输入关键字", "提示", JOptionPane.PLAIN_MESSAGE);
return ;
}
String[] value = BplusTree.testRandomSearch(tree, key);
if(value==null) {
JOptionPane.showMessageDialog(null, "没有此关键词", "提示", JOptionPane.PLAIN_MESSAGE);
return ;
}else {
desc_text.setText(key);
Sc_text.setText(value[0]);
time_text.setText(value[1]);
}
}
});
search_btn.setBounds(373, 47, 85, 30);
contentPanel.add(search_btn);
JLabel lblNewLabel_1 = new JLabel("学号:");
lblNewLabel_1.setFont(new Font("微软雅黑", Font.PLAIN, 16));
lblNewLabel_1.setBounds(59, 140, 110, 30);
contentPanel.add(lblNewLabel_1);
desc_text = new JTextField();
desc_text.setColumns(10);
desc_text.setBounds(192, 140, 255, 30);
contentPanel.add(desc_text);
JLabel lblStockcode = new JLabel("姓名:");
lblStockcode.setFont(new Font("微软雅黑", Font.PLAIN, 16));
lblStockcode.setBounds(59, 200, 110, 30);
contentPanel.add(lblStockcode);
Sc_text = new JTextField();
Sc_text.setColumns(10);
Sc_text.setBounds(192, 200, 255, 30);
contentPanel.add(Sc_text);
JLabel time = new JLabel("用时:");
time.setFont(new Font("微软雅黑", Font.PLAIN, 16));
time.setBounds(59, 252, 110, 30);
contentPanel.add(time);
time_text = new JTextField();
time_text.setColumns(10);
time_text.setBounds(192, 252, 255, 30);
contentPanel.add(time_text);
}
}
package utils;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import com.monitorjbl.xlsx.StreamingReader;
public class ReadFromXlsx {
public static Map<String,String> Load() throws Exception{
InputStream is=new FileInputStream("C:/users/段誉佳/IdeaProjects/index_file/dataset.xlsx");
Map<String,String> maps=new HashMap<String,String>();
Workbook wb=StreamingReader.builder()
.rowCacheSize(100)//缓存到内存中的行数,默认是10
.bufferSize(4096)//读取资源时,缓存到内存的字节大小,默认是1024
.open(is);//打开资源
org.apache.poi.ss.usermodel.Sheet sheet=wb.getSheetAt(0);
String key=null;
String value=null;
Cell cell_k;
Cell cell_v;
//遍历所有的行
//int linesize=0;
for(Row row:sheet){
//遍历所有的列
cell_k=row.getCell(0);//第0列
cell_v=row.getCell(1);//第1列
if(cell_k==null){
value="null";
}else{
key=cell_k.getStringCellValue().trim();
value=cell_v.getStringCellValue().trim();
}
maps.put(key,value);
}
//System.out.println("linesize: "+linesize);
return maps;
}
}