1. 程式人生 > >B樹的java實現

B樹的java實現

1. B樹基本概念

      關於B樹的基本概念、定義可以參考這篇部落格:http://blog.csdn.net/kalikrick/article/details/27980007,本文側重於B樹的java程式碼實現。

1.1 B樹性質

B樹T有如下性質:      1. 每個節點x有如下屬性:
      • x.n表示節點當前key的個數。
      • x中key滿足:x.key1 <= x.key2<= x.key3 <= ....    <= x.keyx,n。也就是x中的key以非降序順序排列。
      • x要麼是葉子節點,要麼是內部節點。
     2. 每個內部節點包含x.n + 1 個引用,指向x.n + 1個孩子節點。葉子節點沒有孩子節點。     3. x的key將孩子節點區分開,也就是滿足:設ki 為 子樹i中的任意key值,k1
<= x.k1 <= k2 <= x.k2 ....<= x.kx.n <= kx.n+1.     4. 所有的葉子節點在同一層                     5. 每個節點擁有的key以及孩子的數量有約束,設整數 t>=2 為最小度:
      • 除根節點外,每個節點必須有至少t-1個key,t個孩子。樹不為空時,根節點至少有一個key。
      • 每個節點至多有2*t-1個key,每個內部節點至多有2*t個孩子。當一個節點有2*t-1個key時,稱其為滿節點。

1.2 B樹內部結點的定義

B樹的型別和節點定義如下圖所示:

 


2. B樹基本操作

2.1 查詢


圖2 -B樹查詢偽碼(來自《演算法導論》)

2.2 插入key

插入示例:
圖4 -B樹插入示例(來自《演算法導論》)該B樹最下度為3,所以節點最多有5個key,最少有2個key。
  • b) 插入B:孩子未滿,直接插入
  • c) 插入Q:孩子已滿,分裂子樹,key T上移到父節點中,然後在將Q插入到適當的孩子中
  • d) 插入L:root已滿,生成新root節點,分裂老root節點,在適當子樹中插入適當孩子中
  • e) 插入F:孩子已滿,分裂子樹,key C上移到父節點,在適當節點中插入Q

2.3 刪除key

刪除示例:
圖5 -刪除操作示例(來自《演算法導論》)

3. 程式碼實現

3.1 B_Tree

package com.oracle.ThreetheenthCharpter;

import

java.util.LinkedList;

import java.util.List;

import java.util.Objects;

public class B_Tree<K extends Comparable<K>,V> {

public BT_Node<K,V> root;

public int degree;    //

public int number;    //樹種結點的數量

public B_Tree(int degree) {

if (degree < 2) { 

throw new IllegalArgumentException("degree must >= 2"); 

root = null;

number = 0;

this.degree = degree;

}

    /**

     * B樹的結點

     */

@SuppressWarnings({"rawtypes","unchecked"})

 class BT_Node<K extends Comparable<K> ,V>{   

BT_Node<K,V> parent;

/*

 *注:這裡的childrenpointers應該用定義成陣列的(因為連結串列的增加和刪除操作比陣列方便,所以引用連結串列),

 *但是實際上,只有B+樹的內部結點才定義了連結串列

 */

final List<Entry<K,V>>  children;   //存放的鍵值對連結串列

final List<BT_Node<K,V>> pointers//子樹指標的連結串列

/**

 *除根節點外,每個節點必須有至少t-1keyt個孩子。樹不為空時,根節點至少有一個key

每個節點至多有2*t-1key,每個內部節點至多有2*t個孩子。當一個節點有2*t-1key時,稱其為滿節點。

 */

public BT_Node() {    

parent = null;

children = new LinkedList<Entry<K,V>>();

pointers = new LinkedList<BT_Node<K,V>>();

}

/**

 *返回鍵值對的數量

 */

public int getKeyNum() {

int keyNum = children.size();

return keyNum;

}

/**

 *判斷當前結點是否已滿

 */

public boolean isFull() {

return this.getKeyNum()==2*degree-1;

}

/**

 *判斷當前結點是否是葉子結點

 */

public boolean isLeafNode() {

return this.pointers.size()==0;

}

/**

 *判斷當前結點的鍵值對數目是否符合約束條件

 */

public boolean isQualified() {

int keyNum = this.getKeyNum();

if(this!=root) {

if(keyNum<(degree-1)) {

throw new IllegalArgumentException("The least keyNum is (degree-1)"); 

}

if(keyNum >(2*degree-1)) {

throw new IllegalArgumentException("The most keyNum is (2*degree-1)"); 

}

}

return true;

}

@Override

public String toString() {

return"BT_Node [children=" + children + "]"+" count:"+this.getKeyNum();

}      

}

/**

 * 返回是否為空

 */

public boolean isEmpty() {

return size()==0;

}

/**

 * 返回容量大小

 */

public int size() {

return number;

}

/**

 * 匿名內部類——鍵值對

 */

private static class Entry<K extends Comparable<K>,V>{

K k;

V v;

public Entry(K k, V v) {

this.k= k;

this.v= v;

}

public final boolean equals(Object o) {

if (o == this)

return true;

if (o instanceof Entry) {

Entry<?,?> e = (Entry<?,?>)o;

if (Objects.equals(k, e.k) &&

Objects.equals(v, e.v))

return true;

}

return false;

}

public K getK() {

return k;

}

public V getV() {

return v;

}

@Override

public String toString() {

return"Entry [k=" + k + ",v=" + v + "]";

}

}


/**

 * 查詢方法

 * B樹的查詢和二叉樹查詢類似,首先在當前節點中查詢,如果沒有並且存在孩子節點,

 * 就遞迴的到可能存在該key的孩子節點中查詢。

 * 不同的是,B樹節點有多個key,需要每個都比較,為了提高效能,可以使用二分法加速節點中的查詢。

 */

public Entry<K,V> search(BT_Node<K,V> x, K key){ 

int i =0;

Entry<K,V> entry,next;

while(i<x.getKeyNum()) {

entry = x.children.get(i);

next = (i==x.getKeyNum()-1)?null:x.children.get(i+1);

//遍歷陣列,如果陣列內的元素包含了目標元素,return

if(key.equals(entry.getK())) {

return entry;

}

if(key.compareTo(x.children.get(x.getKeyNum()-1).getK())>0) {

i=x.getKeyNum();

break;

}else {

//如果沒有包括,則需要遞迴尋找

if(key.compareTo(entry.getK())<0) {

i=0;

break;

}else if(key.compareTo(entry.getK())>0 && key.compareTo(next.getK())<0) {

i=i+1;

break;

}

}

i++;

}

//i對應的是子樹指向的連結串列的索引位置,用一個尾遞迴

if(x.isLeafNode()) {

return null;

}else {

return search(x.pointers.get(i),key);

}

}

    /**

     * 1、先判斷當前節點的Entry是否已滿

       2、如果已滿,就分裂

       3、如果結點是葉子結點做插入操作,不是則向下遞迴

     */

private void insertAction(BT_Node<K,V> current, Entry<K,V> entry) {

//先判斷root結點是否為空

if(root==null) {

root = new BT_Node<K,V>();

current = root;

}

//如果當前結點已滿,需要分裂

if(current.isFull()) {

//分裂

current = split(current);

}

//如果當前結點是葉子結點

if(current.isLeafNode()) {

//插入

int index = getPosition(current, entry);

//System.out.println(index+","+current.getClass());

current.children.add(index, entry);

number++;

return ;

}else{

//否則,向下遞迴尋找

int index = getPosition(current, entry);

//System.out.println("向下遞迴的索引"+index);

insertAction(current.pointers.get(index),entry);

}

}

public void insert(K key, V value) {

        Entry<K,V>entry = new Entry(key,value);

insertAction(root,entry);

}

/**

 * 該方法用於定位在連結串列中,新增Entry結點適合插入的位置

 */

private int getPosition(BT_Node<K,V> x, Entry<K,V> entry) {

List<Entry<K,V>> list = x.children;

int index =0;

Entry<K,V> ent,next;

for(int i=0;i<list.size();i++) {

ent = x.children.get(i);

next = (i==x.getKeyNum()-1)?null:x.children.get(i+1);       

if(entry.k.compareTo(list.get(list.size()-1).k)>0) {

index=list.size();

break;

}else {

//在左邊

if(entry.k.compareTo(ent.k)<=0) {

index=0;

break;

}

//在中間

else if(entry.k.compareTo(next.k)<=0) {

index =i+1;

break;

}

}

}

return index;

}

    /**

     * 分裂

     * B樹的插入需要考慮的一個問題就是當節點以滿時,需要將該節點分裂成兩個節點。

          一個滿的節點有2*t-1個key,內部節點有2*t 個孩子,分裂將其分成兩個各有t-1個key,

          內部節點各t個孩子,多餘的一個節點插入到父節點中,作為分裂之後兩個節點的分割key。

     */

/*

 * 步驟:1、將當前結點的中間Entry結點插入到父節點中,並將剩餘的B_TNode拆分

 * 難點:繫結父結點和子結點的關係;

 *       定義pointer指標

 */

private BT_Node<K,V> split(BT_Node<K,V> x) {

BT_Node<K,V> parent;

BT_Node<K,V> left = new BT_Node<K,V>();

BT_Node<K,V> right = new BT_Node<K,V>();

int len = x.getKeyNum();

Entry<K,V> mid = x.children.get(len/2);

for(int i=0;i<len;i++) {

if(i<len/2) {

left.children.add(x.children.get(i));

}

if(i>len/2) {

right.children.add(x.children.get(i));

}

}              

if(x.isLeafNode()) {

//如果parentnull,說明對根結點做split操作

if(x==root) {

parent = new BT_Node<K,V>();

parent.children.add(mid);

parent.pointers.add(left);

parent.pointers.add(right);

left.parent=parent;

right.parent=parent;

root = parent;

x=root;

}else{

parent = x.parent;    

int position = this.getPosition(parent, mid);

parent.children.add(position,mid);

parent.pointers.remove(position);

parent.pointers.add(position, left);

parent.pointers.add(position+1,right);

left.parent=parent;

right.parent=parent;

x=parent;

}

}else {

if(x==root) {

//繫結父節點

parent = new BT_Node<K,V>();

parent.children.add(mid);

parent.pointers.add(left);

parent.pointers.add(rig