java数据结构与算法总结(三十)--B-tree

原文链接

B-tree(多路搜索树,并不是二叉的)是一种常见的数据结构。使用B-tree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。按照翻译,B 通常认为是Balance的简称。这个数据结构一般用于数据库的索引,综合效率较高。

中文名

多路搜索树

外文名

B-tree

特    点

综合效率较高

属    性

数据结构

目录

结点

性能

用途

B+树


结点

B-tree中,每个结点包含:

1、本结点所含关键字的个数;

2、指向父结点指针

3、关键字;

4、指向子结点的指针;

对于一棵m阶B-tree,每个结点至多可以拥有m个子结点。各结点的关键字和可以拥有的子结点数都有限制,规定m阶B-tree中,根结点至少有2个子结点,除非根结点为叶子节点,相应的,根结点中关键字的个数为1~m-1;非根结点至少有[m/2]([],向上取整)个子结点,相应的,关键字个数为[m/2]-1~m-1。

性能

B-tree有以下特性:

1、关键字集合分布在整棵树中;

2、任何一个关键字出现且只出现在一个结点中;

3、搜索有可能在非叶子结点结束;

4、其搜索性能等价于在关键字全集内做一次二分查找

5、自动层次控制;

由于限制了除根结点以外的非叶子结点,至少含有M/2个儿子,确保了结点的至少利用率,其最低搜索性能为:

B-tree最低搜索性能B-tree最低搜索性能

其中,M为设定的非叶子结点最多子树个数,N为关键字总数;

所以B-树的性能总是等价于二分查找(与M值无关),也就没有B树平衡的问题;

由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占M/2的结点;删除结点时,需将两个不足M/2的兄弟结点合并。

用途

鉴于B-tree具有良好的定位特性,其常被用于对检索时间要求苛刻的场合,例如:

1、B-tree索引是数据库中存取和查找文件(称为记录或键值)的一种方法。

2、硬盘中的结点也是B-tree结构的。与内存相比,硬盘必须花成倍的时间来存取一个数据元素,这是因为硬盘的机械部件读写数据的速度远远赶不上纯电子媒体的内存。与一个结点两个分支的二元树相比,B-tree利用多个分支(称为子树)的结点,减少获取记录时所经历的结点数,从而达到节省存取时间的目的。

/* btrees.h */
 
/*
 * 平衡多路树的一种重要方案。
 * 在 1970 年由 R. Bayer 和 E. McCreight 发明。
 */
#define M 1
 
/* B 树的阶,即非根节点中键的最小数目。
 * 有些人把阶定义为非根节点中子树的最大数目。
 */
typedef int typekey;
typedef struct btnode {                 /* B-Tree 节点 */
    int        d;              /* 节点中键的数目 */
    typekey        k[2 * M];       /* 键 */
    char        *v[2 * M];      /* 值 */
    struct btnode    *p[2 * M + 1];  /* 指向子树的指针 */
} node, *btree;
 
/*
 * 每个键的左子树中的所有的键都小于这个键,
 * 每个键的右子树中的所有的键都大于等于这个键。
 * 叶子节点中的每个键都没有子树。
 */
 
/* 当 M 等于 1 时也称为 2-3 树
 * +----+----+
 * | k0 | k1 |
 * +-+----+----+---
 * | p0 | p1 | p2 |
 * +----+----+----+
 */
extern int    btree_disp;     /* 查找时找到的键在节点中的位置 */
extern char    * InsValue;     /* 与要插的键相对应的值 */
extern btree search( typekey, btree );
 
extern btree insert( typekey, btree );
 
extern btree delete( typekey, btree );
 
extern int height( btree );
 
extern int count( btree );
 
extern double payload( btree );
 
extern btree deltree( btree );
 
/* end of btrees.h */
/*******************************************************/
/* btrees.c */
#include <stdlib.h>
#include <stdio.h>
#include "btrees.h"
btree search( typekey, btree );
 
btree insert( typekey, btree );
 
btree delete( typekey, btree );
 
int height( btree );
 
int count( btree );
 
double payload( btree );
 
btree deltree( btree );
 
static void InternalInsert( typekey, btree );
 
static void InsInNode( btree, int );
 
static void SplitNode( btree, int );
 
static btree NewRoot( btree );
 
static void InternalDelete( typekey, btree );
 
static void JoinNode( btree, int );
 
 
static void MoveLeftNode( btree t, int );
 
static void MoveRightNode( btree t, int );
 
static void DelFromNode( btree t, int );
 
static btree FreeRoot( btree );
 
static btree delall( btree );
 
static void Error( int, typekey );
 
int        btree_disp;             /* 查找时找到的键在节点中的位置 */
char        * InsValue = NULL;      /* 与要插的键相对应的值 */
static int    flag;                   /* 节点增减标志 */
static int    btree_level    = 0;    /* 多路树的高度 */
static int    btree_count    = 0;    /* 多路树的键总数 */
static int    node_sum    = 0;    /* 多路树的节点总数 */
static int    level;                  /* 当前访问的节点所处的高度 */
static btree    NewTree;                /* 在节点分割的时候指向新建的节点 */
static typekey    InsKey;                 /* 要插入的键 */
btree search( typekey key, btree t ) {
    int i, j, m;
    level = btree_level - 1;
    while ( level >= 0 ) {
        for ( i = 0, j = t->d - 1; i < j; m = (j + i) / 2, (key > t->k[m]) ? (i = m + 1) : (j = m) )
            ;
        if ( key == t->k [i] ) {
            btree_disp = i;
            return(t);
        }
        if ( key > t->k [i] ) /* i == t->d-1 时有可能出现 */
            i++;
        t = t->p[i];
        level--;
    }
    return(NULL);
}
 
btree insert( typekey key, btree t ) {
    level = btree_level;
    InternalInsert( key, t );
    if ( flag == 1 )                /* 根节点满之后,它被分割成两个半满节点 */
        t = NewRoot( t );       /* 树的高度增加 */
    return(t);
}
 
 
void InternalInsert( typekey key, btree t ) {
    int i, j, m;
    level--;
    if ( level < 0 ) {              /* 到达了树的底部: 指出要做的插入 */
        NewTree = NULL;         /* 这个键没有对应的子树 */
        InsKey    = key;          /* 导致底层的叶子节点增加键值+空子树对 */
        btree_count++;
        flag = 1;               /* 指示上层节点把返回的键插入其中 */
        return;
    }
    for ( i = 0, j = t->d - 1; i < j; m = (j + i) / 2, (key > t->k[m]) ? (i = m + 1) : (j = m) )
        ;
    if ( key == t->k[i] ) {
        Error( 1, key );        /* 键已经在树中 */
        flag = 0;
        return;
    }
    if ( key > t->k[i] )            /* i == t->d-1 时有可能出现 */
        i++;
    InternalInsert( key, t->p[i] );
    if ( flag == 0 )
        return;
/* 有新键要插入到当前节点中 */
    if ( t->d < 2 * M ) {           /* 当前节点未满 */
        InsInNode( t, i );      /* 把键值+子树对插入当前节点中 */
        flag = 0;               /* 指示上层节点没有需要插入的键值+子树,插入过程结束 */
    } else                          /* 当前节点已满,则分割这个页面并把键值+子树对插入当前节点中 */
        SplitNode( t, i );      /* 继续指示上层节点把返回的键值+子树插入其中 */
}
 
/*
 * 把一个键和对应的右子树插入一个节点中
 */
void InsInNode( btree t, int d ) {
    int i;
/* 把所有大于要插入的键值的键和对应的右子树右移 */
    for ( i = t->d; i > d; i-- ) {
        t->k[i]        = t->k[i - 1];
        t->v[i]        = t->v[i - 1];
        t->p[i + 1]    = t->p[i];
    }
/* 插入键和右子树 */
    t->k[i]        = InsKey;
    t->p[i + 1]    = NewTree;
    t->v[i]        = InsValue;
    t->d++;
}
 
 
/*
 * 前件是要插入一个键和对应的右子树,并且本节点已经满
 * 导致分割这个节点,插入键和对应的右子树,
 * 并向上层返回一个要插入键和对应的右子树
 */
void SplitNode( btree t, int d ) {
    int    i, j;
    btree    temp;
    typekey temp_k;
    char    *temp_v;
/* 建立新节点 */
    temp = (btree) malloc( sizeof(node) );
 
 
/*
 * +---+--------+-----+-----+--------+-----+
 * | 0 | ...... | M | M+1 | ...... |2*M-1|
 * +---+--------+-----+-----+--------+-----+
 * |<- M+1 ->|<- M-1 ->|
 */
    if ( d > M ) { /* 要插入当前节点的右半部分 */
/* 把从 2*M-1 到 M+1 的 M-1 个键值+子树对转移到新节点中,
 * 并且为要插入的键值+子树空出位置 */
        for ( i = 2 * M - 1, j = M - 1; i >= d; i--, j-- ) {
            temp->k[j]    = t->k[i];
            temp->v[j]    = t->v[i];
            temp->p[j + 1]    = t->p[i + 1];
        }
        for ( i = d - 1, j = d - M - 2; j >= 0; i--, j-- ) {
            temp->k[j]    = t->k[i];
            temp->v[j]    = t->v[i];
            temp->p[j + 1]    = t->p[i + 1];
        }
/* 把节点的最右子树转移成新节点的最左子树 */
        temp->p[0] = t->p[M + 1];
/* 在新节点中插入键和右子树 */
        temp->k[d - M - 1]    = InsKey;
        temp->p[d - M]        = NewTree;
        temp->v[d - M - 1]    = InsValue;
/* 设置要插入上层节点的键和值 */
        InsKey        = t->k[M];
        InsValue    = t->v[M];
    } else  { /* d <= M */
/* 把从 2*M-1 到 M 的 M 个键值+子树对转移到新节点中 */
        for ( i = 2 * M - 1, j = M - 1; j >= 0; i--, j-- ) {
            temp->k[j]    = t->k[i];
            temp->v[j]    = t->v[i];
            temp->p[j + 1]    = t->p[i + 1];
        }
        if ( d == M )   /* 要插入当前节点的正中间 */
/* 把要插入的子树作为新节点的最左子树 */
            temp->p[0] = NewTree;
/* 直接把要插入的键和值返回给上层节点 */
        else {           /* (d<M) 要插入当前节点的左半部分 */
/* 把节点当前的最右子树转移成新节点的最左子树 */
            temp->p[0] = t->p[M];
/* 保存要插入上层节点的键和值 */
            temp_k    = t->k[M - 1];
            temp_v    = t->v[M - 1];
/* 把所有大于要插入的键值的键和对应的右子树右移 */
            for ( i = M - 1; i > d; i-- ) {
                t->k[i]        = t->k[i - 1];
                t->v[i]        = t->v[i - 1];
                t->p[i + 1]    = t->p[i];
            }
/* 在节点中插入键和右子树 */
            t->k[d]        = InsKey;
            t->p[d + 1]    = NewTree;
            t->v[d]        = InsValue;
/* 设置要插入上层节点的键和值 */
            InsKey        = temp_k;
            InsValue    = temp_v;
        }
    }
    t->d    = M;
    temp->d = M;
    NewTree = temp;
    node_sum++;
}
 
 
btree delete( typekey key, btree t ) {
    level = btree_level;
    InternalDelete( key, t );
    if ( t->d == 0 )
/* 根节点的子节点合并导致根节点键的数目随之减少,
 * 当根节点中没有键的时候,只有它的最左子树可能非空 */
        t = FreeRoot( t );
    return(t);
}
 
 
void InternalDelete( typekey key, btree t ) {
    int    i, j, m;
    btree    l, r;
    int    lvl;
    level--;
    if ( level < 0 ) {
        Error( 0, key );                /* 在整个树中未找到要删除的键 */
        flag = 0;
        return;
    }
    for ( i = 0, j = t->d - 1; i < j; m = (j + i) / 2, (key > t->k[m]) ? (i = m + 1) : (j = m) )
        ;
    if ( key == t->k[i] )  {                /* 找到要删除的键 */
        if ( t->v[i] != NULL )
            free( t->v[i] );        /* 释放这个节点包含的值 */
        if ( level == 0 ) {              /* 有子树为空则这个键位于叶子节点 */
            DelFromNode( t, i );
            btree_count--;
            flag = 1;
/* 指示上层节点本子树的键数量减少 */
            return;
        } else  { /* 这个键位于非叶节点 */
            lvl = level - 1;
/* 找到前驱节点 */
            r = t->p[i];
            while ( lvl > 0 ) {
                r = r->p[r->d];
                lvl--;
            }
            t->k[i]        = r->k[r->d - 1];
            t->v[i]        = r->v[r->d - 1];
            r->v[r->d - 1]    = NULL;
            key        = r->k[r->d - 1];
        }
    } else if ( key > t->k[i] ) /* i == t->d-1 时有可能出现 */
        i++;
    InternalDelete( key, t->p[i] );
/* 调整平衡 */
    if ( flag == 0 )
        return;
    if ( t->p[i]->d < M ) {
        if ( i == t->d )        /* 在最右子树中发生了删除 */
            i--;            /* 调整最右键的左右子树平衡 */
        l    = t->p [i];
        r    = t->p[i + 1];
        if ( r->d > M )
            MoveLeftNode( t, i );
        else if ( l->d > M )
            MoveRightNode( t, i );
        else {
            JoinNode( t, i );
/* 继续指示上层节点本子树的键数量减少 */
            return;
        }
        flag = 0;
/* 指示上层节点本子树的键数量没有减少,删除过程结束 */
    }
}
 
/*
 * 合并一个节点的某个键对应的两个子树
 */
void JoinNode( btree t, int d ) {
    btree    l, r;
    int    i, j;
    l    = t->p[d];
    r    = t->p[d + 1];
/* 把这个键下移到它的左子树 */
    l->k[l->d]    = t->k[d];
    l->v[l->d]    = t->v[d];
/* 把右子树中的所有键值和子树转移到左子树 */
    for ( j = r->d - 1, i = l->d + r->d; j >= 0; j--, i-- ) {
        l->k[i] = r->k[j];
        l->v[i] = r->v[j];
        l->p[i] = r->p[j];
    }
    l->p[l->d + r->d + 1]    = r->p[r->d];
    l->d            += r->d + 1;
/* 释放右子树的节点 */
    free( r );
/* 把这个键右边的键和对应的右子树左移 */
    for ( i = d; i < t->d - 1; i++ ) {
        t->k[i]        = t->k[i + 1];
        t->v[i]        = t->v[i + 1];
        t->p[i + 1]    = t->p[i + 2];
    }
    t->d--;
    node_sum--;
}
 
/*
 * 从一个键的右子树向左子树转移一些键,使两个子树平衡
 */
void MoveLeftNode( btree t, int d ) {
    btree    l, r;
    int    m; /* 应转移的键的数目 */
    int    i, j;
    l    = t->p[d];
    r    = t->p[d + 1];
    m    = (r->d - l->d) / 2;
/* 把这个键下移到它的左子树 */
    l->k[l->d]    = t->k[d];
    l->v[l->d]    = t->v[d];
 
/* 把右子树的最左子树转移成左子树的最右子树
 * 从右子树向左子树移动 m-1 个键+子树对 */
    for ( j = m - 2, i = l->d + m - 1; j >= 0; j--, i-- ) {
        l->k[i] = r->k[j];
        l->v[i] = r->v[j];
        l->p[i] = r->p[j];
    }
    l->p[l->d + m] = r->p[m - 1];
/* 把右子树的最左键提升到这个键的位置上 */
    t->k[d] = r->k[m - 1];
    t->v[d] = r->v[m - 1];
/* 把右子树中的所有键值和子树左移 m 个位置 */
    r->p[0] = r->p[m];
    for ( i = 0; i < r->d - m; i++ ) {
        r->k[i] = r->k[i + m];
        r->v[i] = r->v[i + m];
        r->p[i] = r->p[i + m];
    }
    r->p[r->d - m]    = r->p[r->d];
    l->d        += m;
    r->d        -= m;
}
 
/*
 * 从一个键的左子树向右子树转移一些键,使两个子树平衡
 */
void MoveRightNode( btree t, int d ) {
    btree    l, r;
    int    m; /* 应转移的键的数目 */
    int    i, j;
    l    = t->p[d];
    r    = t->p[d + 1];
    m    = (l->d - r->d) / 2;
/* 把右子树中的所有键值和子树右移 m 个位置 */
    r->p[r->d + m] = r->p[r->d];
    for ( i = r->d - 1; i >= 0; i-- ) {
        r->k[i + m]    = r->k[i];
        r->v[i + m]    = r->v[i];
        r->p[i + m]    = r->p[i];
    }
/* 把这个键下移到它的右子树 */
    r->k[m - 1]    = t->k[d];
    r->v[m - 1]    = t->v[d];
/* 把左子树的最右子树转移成右子树的最左子树 */
    r->p[m - 1] = l->p[l->d];
/* 从左子树向右子树移动 m-1 个键+子树对 */
    for ( i = l->d - 1, j = m - 2; j >= 0; j--, i-- ) {
        r->k[j] = l->k[i];
        r->v[j] = l->v[i];
        r->p[j] = l->p[i];
    }
/* 把左子树的最右键提升到这个键的位置上 */
    t->k[d] = l->k[i];
    t->v[d] = l->v[i];
    l->d    -= m;
    r->d    += m;
}
 
 
/*
 * 把一个键和对应的右子树从一个节点中删除
 */
void DelFromNode( btree t, int d ) {
    int i;
/* 把所有大于要删除的键值的键左移 */
    for ( i = d; i < t->d - 1; i++ ) {
        t->k[i] = t->k[i + 1];
        t->v[i] = t->v[i + 1];
    }
    t->d--;
}
 
/*
 * 建立有两个子树和一个键的根节点
 */
btree NewRoot( btree t ) {
    btree temp;
    temp        = (btree) malloc( sizeof(node) );
    temp->d        = 1;
    temp->p[0]    = t;
    temp->p[1]    = NewTree;
    temp->k[0]    = InsKey;
    temp->v[0]    = InsValue;
    btree_level++;
    node_sum++;
    return(temp);
}
 
/*
 * 释放根节点,并返回它的最左子树
 */
btree FreeRoot( btree t ) {
    btree temp;
    temp = t->p[0];
    free( t );
    btree_level--;
    node_sum--;
    return(temp);
}
 
void Error( int f, typekey key ) {
    if ( f )
        printf( "Btrees error: Insert %d!\n", key );
    else
        printf( "Btrees error: delete %d!\n", key );
}
 
int height( btree t ) {
    return(btree_level);
}
 
int count( btree t ) {
    return(btree_count);
}
 
double payload( btree t ) {
    if ( node_sum == 0 )
        return(1);
    return( (double) btree_count / (node_sum * (2 * M) ) );
}
 
btree deltree( btree t ) {
    level        = btree_level;
    btree_level    = 0;
    return(delall( t ) );
}
 
btree delall( btree t ) {
    int i;
    level--;
    if ( level >= 0 ) {
        for ( i = 0; i < t->d; i++ )
            if ( t->v[i] != NULL )
                free( t->v[i] );
        if ( level > 0 )
            for ( i = 0; i <= t->d; i++ )
                t->p[i] = delall( t->p[i] );
        free( t );
    }
    return(NULL);
}
 
/* end of btrees.c */

B+树

另外还有一种与此类似的树结构叫B+树,像 Berkerly DB , sqlite , mysql 数据库都使用了B+树算法处理索引。

B+和B-(即B)是因为每个结点上的关键字不同。一个多一个,一个少一个。

对于B+树,其结点结构与B-tree相同,不同的是各结点的关键字和可以拥有的子结点数。如m阶B+树中,每个结点至多可以拥有m个子结点。非根结点至少有[m/2]个子结点,而关键字个数比B-tree多一个,为[m/2]~m。

这两种处理索引的数据结构的不同之处:

1。B树中同一键值不会出现多次,并且它有可能出现在叶结点,也有可能出现在非叶结点中。而B+树的键一定会出现在叶结点中,并且有可能在非叶结点中也有可能重复出现,以维持B+树的平衡。

2。因为B树键位置不定,且在整个树结构中只出现一次,虽然可以节省存储空间,但使得在插入、删除操作复杂度明显增加。B+树相比来说是一种较好的折中。

3。B树的查询效率与键在树中的位置有关,最大时间复杂度与B+树相同(在叶结点的时候),最小时间复杂度为1(在根结点的时候)。而B+树的时间复杂度对某建成的树是固定的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BPlusTree_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; } }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值