go 語言版 B + 樹
阿新 • • 發佈:2018-12-25
在搞的專案需要實現一顆B + 樹 來在原有開源專案的基礎上實現新增資料結構的儲存。搜尋了一圈發現網上沒有用go語言寫的版本,其他語言的版本也都有或多或少的bug,於是自己實現了一個,測試了一下應該沒問題。
package main import ( "fmt" ) const M = 4 const INT_MAX = int(^uint(0) >> 1) const INT_MIN = ^INT_MAX const LIMIT_M_2 = (M + 1)/2 type Position *BPlusFullNode type BPlusLeafNode struct { Next *BPlusFullNode datas []int }
//葉子節點應該為Children為空,但leafNode中datas不為空 Next一般不為空 type BPlusFullNode struct { KeyNum int Key []int isLeaf bool Children []*BPlusFullNode leafNode *BPlusLeafNode } type BPlusTree struct { keyMax int root *BPlusFullNode ptr *BPlusFullNode } func MallocNewNode(isLeaf bool) *BPlusFullNode { var NewNode *BPlusFullNode if isLeaf == true { NewLeaf := MallocNewLeaf() NewNode = &BPlusFullNode{ KeyNum: 0, Key: make([]int, M + 1), //申請M + 1是因為插入時可能暫時出現節點key大於M 的情況,待後期再分裂處理 isLeaf: isLeaf, Children: nil, leafNode: NewLeaf, } }else{ NewNode = &BPlusFullNode{ KeyNum: 0, Key: make([]int, M + 1), isLeaf: isLeaf, Children: make([]*BPlusFullNode, M + 1), leafNode: nil, } } for i,_ := range NewNode.Key{ NewNode.Key[i] = INT_MIN } return NewNode } func MallocNewLeaf() *BPlusLeafNode{ NewLeaf := BPlusLeafNode{ Next: nil, datas: make([]int, M + 1), } for i,_ := range NewLeaf.datas { NewLeaf.datas[i] = i } return &NewLeaf } func(tree *BPlusTree) Initialize() { /* 根結點 */ T := MallocNewNode(true) tree.ptr = T tree.root = T } func FindMostLeft(P Position) Position{ var Tmp Position Tmp = P if Tmp.isLeaf == true || Tmp == nil{ return Tmp }else if Tmp.Children[0].isLeaf == true{ return Tmp.Children[0] }else{ for (Tmp != nil && Tmp.Children[0].isLeaf != true ) { Tmp = Tmp.Children[0] } } return Tmp.Children[0] } func FindMostRight(P Position ) Position{ var Tmp Position Tmp = P if Tmp.isLeaf == true || Tmp == nil{ return Tmp }else if Tmp.Children[Tmp.KeyNum - 1].isLeaf == true{ return Tmp.Children[Tmp.KeyNum - 1] }else{ for (Tmp != nil && Tmp.Children[Tmp.KeyNum - 1].isLeaf != true ) { Tmp = Tmp.Children[Tmp.KeyNum - 1] } } return Tmp.Children[Tmp.KeyNum - 1] } /* 尋找一個兄弟節點,其儲存的關鍵字未滿,若左右都滿返回nil */ func FindSibling(Parent Position,i int ) Position{ var Sibling Position var upperLimit int upperLimit = M Sibling = nil if i == 0{ if Parent.Children[1].KeyNum < upperLimit{ Sibling = Parent.Children[1] } } else if (Parent.Children[i - 1].KeyNum < upperLimit){ Sibling = Parent.Children[i - 1] }else if (i + 1 < Parent.KeyNum && Parent.Children[i + 1].KeyNum < upperLimit){ Sibling = Parent.Children[i + 1] } return Sibling } /* 查詢兄弟節點,其關鍵字數大於M/2 ;沒有返回nil j用來標識是左兄還是右兄*/ func FindSiblingKeyNum_M_2( Parent Position,i int, j *int) Position{ var lowerLimit int var Sibling Position Sibling = nil lowerLimit = LIMIT_M_2 if (i == 0){ if (Parent.Children[1].KeyNum > lowerLimit){ Sibling = Parent.Children[1] *j = 1 } }else{ if (Parent.Children[i - 1].KeyNum > lowerLimit){ Sibling = Parent.Children[i - 1] *j = i - 1 } else if (i + 1 < Parent.KeyNum && Parent.Children[i + 1].KeyNum > lowerLimit){ Sibling = Parent.Children[i + 1] *j = i + 1 } } return Sibling } /* 當要對X插入data的時候,i是X在Parent的位置,insertIndex是data要插入的位置,j可由查詢得到 當要對Parent插入X節點的時候,posAtParent是要插入的位置,Key和j的值沒有用 */ func(tree *BPlusTree) InsertElement (isData bool,Parent Position, X Position, Key int, posAtParent int , insertIndex int, data int) Position { var k int if (isData){ /* 插入data*/ k = X.KeyNum - 1 for (k >= insertIndex){ X.Key[k + 1] = X.Key[k] X.leafNode.datas[k + 1] = X.leafNode.datas[k] k-- } X.Key[insertIndex] = Key X.leafNode.datas[insertIndex] = data if (Parent != nil) { Parent.Key[posAtParent] = X.Key[0] //可能min_key 已發生改變 } X.KeyNum++ }else{ /* 插入節點 */ /* 對樹葉節點進行連線 */ if (X.isLeaf == true){ if (posAtParent > 0){ Parent.Children[posAtParent- 1].leafNode.Next = X } X.leafNode.Next = Parent.Children[posAtParent] //更新葉子指標 if X.Key[0] <= tree.ptr.Key[0]{ tree.ptr = X } } k = Parent.KeyNum - 1 for (k >= posAtParent){ //插入節點時key也要對應的插入 Parent.Children[k + 1] = Parent.Children[k] Parent.Key[k + 1] = Parent.Key[k] k-- } Parent.Key[posAtParent] = X.Key[0] Parent.Children[posAtParent] = X Parent.KeyNum++ } return X }
/* 兩個引數X posAtParent 有些重複 posAtParent可以通過X的最小關鍵字查詢得到 */ func(tree *BPlusTree) RemoveElement(isData bool,Parent Position ,X Position , posAtParent int, deleteIndex int ) Position { var k,keyNum int if (isData){ keyNum = X.KeyNum /* 刪除key */ k = deleteIndex + 1 for (k < keyNum){ X.Key[k - 1] = X.Key[k] X.leafNode.datas[k - 1] = X.leafNode.datas[k] k++ } X.Key[keyNum - 1] = INT_MIN X.leafNode.datas[keyNum - 1] = INT_MIN Parent.Key[posAtParent] = X.Key[0] X.KeyNum-- }else{ /* 刪除節點 */ /* 修改樹葉節點的連結 */ if (X.isLeaf == true && posAtParent > 0){ Parent.Children[posAtParent - 1].leafNode.Next = Parent.Children[posAtParent + 1] } keyNum = Parent.KeyNum k = posAtParent + 1 for (k < keyNum){ Parent.Children[k - 1] = Parent.Children[k] Parent.Key[k - 1] = Parent.Key[k] k++ } if X.Key[0] == tree.ptr.Key[0]{ // refresh ptr tree.ptr = Parent.Children[0] } Parent.Children[Parent.KeyNum - 1] = nil Parent.Key[Parent.KeyNum - 1] = INT_MIN Parent.KeyNum-- } return X } /* Src和Dst是兩個相鄰的節點,posAtParent是Src在Parent中的位置; 將Src的元素移動到Dst中 ,eNum是移動元素的個數*/ func(tree *BPlusTree) MoveElement(src Position , dst Position , parent Position , posAtParent int,eNum int ) Position { var TmpKey,data int var Child Position var j int var srcInFront bool srcInFront = false if (src.Key[0] < dst.Key[0]) { srcInFront = true } j = 0 /* 節點Src在Dst前面 */ if (srcInFront){ if (src.isLeaf == false){ for (j < eNum) { Child = src.Children[src.KeyNum - 1] tree.RemoveElement(false, src, Child, src.KeyNum - 1, INT_MIN) //每刪除一個節點keyNum也自動減少1 隊尾刪 tree.InsertElement(false, dst, Child, INT_MIN, 0, INT_MIN,INT_MIN) //隊頭加 j++ } }else{ for (j < eNum) { TmpKey = src.Key[src.KeyNum -1] data = src.leafNode.datas[src.KeyNum - 1] tree.RemoveElement(true, parent, src, posAtParent, src.KeyNum - 1) tree.InsertElement(true, parent, dst, TmpKey, posAtParent + 1, 0,data) j++ } } parent.Key[posAtParent+ 1] = dst.Key[0] /* 將樹葉節點重新連線 */ if (src.KeyNum > 0) { FindMostRight(src).leafNode.Next = FindMostLeft(dst) //似乎不需要重連,src的最右本身就是dst最左的上一元素 }else { if src.isLeaf == true { parent.Children[posAtParent - 1 ].leafNode.Next = dst } // 此種情況肯定是merge merge中有實現先移動再刪除操作 //tree.RemoveElement(false ,parent.parent,parent ,parentIndex,INT_MIN ) } }else{ if (src.isLeaf == false){ for (j < eNum) { Child = src.Children[0] tree.RemoveElement(false, src, Child, 0, INT_MIN) //從src的隊頭刪 tree.InsertElement(false, dst, Child, INT_MIN, dst.KeyNum, INT_MIN,INT_MIN) j++ } }else{ for (j < eNum) { TmpKey = src.Key[0] data = src.leafNode.datas[0] tree.RemoveElement(true, parent, src, posAtParent, 0) tree.InsertElement(true, parent, dst, TmpKey, posAtParent - 1, dst.KeyNum,data) j++ } } parent.Key[posAtParent] = src.Key[0] if (src.KeyNum > 0) { FindMostRight(dst).leafNode.Next = FindMostLeft(src) }else { if src.isLeaf == true { dst.leafNode.Next = src.leafNode.Next } //tree.RemoveElement(false ,parent.parent,parent ,parentIndex,INT_MIN ) } } return parent } //i為節點X的位置 func(tree *BPlusTree) SplitNode(Parent Position, beSplitedNode Position, i int) Position{ var j,k, keyNum int var NewNode Position if beSplitedNode.isLeaf == true { NewNode = MallocNewNode(true) }else{ NewNode = MallocNewNode(false) } k = 0 j = beSplitedNode.KeyNum / 2 keyNum = beSplitedNode.KeyNum for (j < keyNum){ if (beSplitedNode.isLeaf == false){ //Internal node NewNode.Children[k] = beSplitedNode.Children[j] beSplitedNode.Children[j] = nil }else { NewNode.leafNode.datas[k] = beSplitedNode.leafNode.datas[j] beSplitedNode.leafNode.datas[j] = INT_MIN } NewNode.Key[k] = beSplitedNode.Key[j] beSplitedNode.Key[j] = INT_MIN NewNode.KeyNum++ beSplitedNode.KeyNum-- j++ k++ } if (Parent != nil) { tree.InsertElement(false, Parent, NewNode, INT_MIN, i+1, INT_MIN, INT_MIN) // parent > limit 時的遞迴split recurvie中實現 }else{ /* 如果是X是根,那麼建立新的根並返回 */ Parent = MallocNewNode(false) tree.InsertElement(false , Parent, beSplitedNode, INT_MIN, 0, INT_MIN, INT_MIN) tree.InsertElement(false, Parent, NewNode,INT_MIN, 1, INT_MIN,INT_MIN) tree.root = Parent return Parent } return beSplitedNode // 為什麼返回一個X一個Parent? } /* 合併節點,X少於M/2關鍵字,S有大於或等於M/2個關鍵字*/ func(tree * BPlusTree) MergeNode( Parent Position, X Position, S Position, i int) Position{ var Limit int /* S的關鍵字數目大於M/2 */ if (S.KeyNum > LIMIT_M_2){ /* 從S中移動一個元素到X中 */ tree.MoveElement(S, X, Parent, i,1) }else{ /* 將X全部元素移動到S中,並把X刪除 */ Limit = X.KeyNum tree.MoveElement(X,S, Parent, i,Limit) //最多時S恰好MAX MoveElement已考慮了parent.key的索引更新 tree.RemoveElement(false, Parent, X, i, INT_MIN) } return Parent } func(tree *BPlusTree) RecursiveInsert( beInsertedElement Position, Key int, posAtParent int , Parent Position,data int) (Position, bool){ var InsertIndex,upperLimit int var Sibling Position var result bool result = true /* 查詢分支 */ InsertIndex = 0 for (InsertIndex < beInsertedElement.KeyNum && Key >= beInsertedElement.Key[InsertIndex]){ /* 重複值不插入 */ if (Key == beInsertedElement.Key[InsertIndex]){ return beInsertedElement ,false } InsertIndex++ } //key必須大於被插入節點的最小元素,才能插入到此節點,故需回退一步 if (InsertIndex != 0 && beInsertedElement.isLeaf == false) { InsertIndex-- } /* 樹葉 */ if (beInsertedElement.isLeaf == true) { beInsertedElement = tree.InsertElement(true, Parent, beInsertedElement, Key, posAtParent, InsertIndex,data) //返回葉子節點 /* 內部節點 */ }else { beInsertedElement.Children[InsertIndex],result = tree.RecursiveInsert(beInsertedElement.Children[InsertIndex], Key, InsertIndex, beInsertedElement,data) //更新parent發生在split時 } /* 調整節點 */ upperLimit = M if (beInsertedElement.KeyNum > upperLimit){ /* 根 */ if (Parent == nil){ /* 分裂節點 */ beInsertedElement = tree.SplitNode(Parent, beInsertedElement, posAtParent) } else{ Sibling = FindSibling(Parent, posAtParent) if (Sibling != nil){ /* 將T的一個元素(Key或者Child)移動的Sibing中 */ tree.MoveElement(beInsertedElement, Sibling, Parent, posAtParent, 1) }else{ /* 分裂節點 */ beInsertedElement = tree.SplitNode(Parent, beInsertedElement, posAtParent) } } } if (Parent != nil) { Parent.Key[posAtParent] = beInsertedElement.Key[0] } return beInsertedElement, result } /* 插入 */ func(tree *BPlusTree) Insert( Key int,data int) (Position,bool){ return tree.RecursiveInsert(tree.root, Key, 0, nil, data) //從根節點開始插入 } func(tree *BPlusTree) RecursiveRemove( beRemovedElement Position, Key int, posAtParent int, Parent Position) (Position, bool){ var deleteIndex int var Sibling Position var NeedAdjust bool var result bool Sibling = nil /* 查詢分支 TODO查詢函式可以在參考這裡的程式碼 或者實現一個遞迴遍歷*/ deleteIndex = 0 for (deleteIndex < beRemovedElement.KeyNum && Key >= beRemovedElement.Key[deleteIndex]){ if (Key == beRemovedElement.Key[deleteIndex]) { break } deleteIndex++ } if (beRemovedElement.isLeaf == true){ /* 沒找到 */ if (Key != beRemovedElement.Key[deleteIndex] || deleteIndex == beRemovedElement.KeyNum) { return beRemovedElement, false } }else { if (deleteIndex == beRemovedElement.KeyNum || Key < beRemovedElement.Key[deleteIndex]) { deleteIndex-- //準備到下層節點查詢 } } /* 樹葉 */ if (beRemovedElement.isLeaf == true){ beRemovedElement = tree.RemoveElement(true, Parent, beRemovedElement, posAtParent, deleteIndex) }else{ beRemovedElement.Children[deleteIndex],result = tree.RecursiveRemove(beRemovedElement.Children[deleteIndex], Key, deleteIndex, beRemovedElement) } NeedAdjust = false //有子節點的root節點,當keyNum小於2時 if (Parent == nil && beRemovedElement.isLeaf == false && beRemovedElement.KeyNum < 2){ NeedAdjust = true } else if (Parent != nil && beRemovedElement.isLeaf == false && beRemovedElement.KeyNum < LIMIT_M_2){ /* 除根外,所有中間節點的兒子數不在[M/2]到M之間時。(符號[]表示向上取整) */ NeedAdjust = true } else if (Parent != nil && beRemovedElement.isLeaf == true && beRemovedElement.KeyNum < LIMIT_M_2){ /* (非根)樹葉中關鍵字的個數不在[M/2]到M之間時 */ NeedAdjust = true } /* 調整節點 */ if (NeedAdjust){ /* 根 */ if (Parent == nil){ if(beRemovedElement.isLeaf == false && beRemovedElement.KeyNum < 2){ //樹根的更新操作 樹高度減一 beRemovedElement = beRemovedElement.Children[0] tree.root = beRemovedElement.Children[0] return beRemovedElement,true } }else{ /* 查詢兄弟節點,其關鍵字數目大於M/2 */ Sibling = FindSiblingKeyNum_M_2(Parent, posAtParent,&deleteIndex) if (Sibling != nil){ tree.MoveElement(Sibling, beRemovedElement, Parent, deleteIndex, 1) }else{ if (posAtParent == 0){ Sibling = Parent.Children[1] } else{ Sibling = Parent.Children[posAtParent- 1] } Parent = tree.MergeNode(Parent, beRemovedElement, Sibling, posAtParent) //Merge中已考慮空節點的刪除 beRemovedElement = Parent.Children[posAtParent] } } } return beRemovedElement ,result } /* 刪除 */ func(tree *BPlusTree) Remove(Key int) (Position,bool){ return tree.RecursiveRemove(tree.root, Key, 0, nil) } func(tree *BPlusTree) FindData(key int) (int, bool) { var currentNode *BPlusFullNode var index int currentNode = tree.root for index < currentNode.KeyNum { index = 0 for key >= currentNode.Key[index] && index < currentNode.KeyNum{ index ++ } if index == 0 { return INT_MIN, false }else{ index-- if currentNode.isLeaf == false { currentNode = currentNode.Children[index] }else{ if key == currentNode.Key[index] { return currentNode.leafNode.datas[index], true }else{ return INT_MIN, false } } } } return INT_MIN, false } func main (){ var tree BPlusTree (&tree).Initialize() var i int i = 1 for i < 100 { _ ,result := tree.Insert(i,i * 10) fmt.Print(i) if result == false { print("資料已存在") } i++ } tree.Remove(7) tree.Remove(6) tree.Remove(5) resultDate,success:=tree.FindData(5) if success == true { fmt.Print(resultDate) fmt.Printf("\n") } //遍歷結點元素 i = 0 for i < tree.root.Children[1].KeyNum{ fmt.Println(tree.root.Children[1].leafNode.datas[i]) i++ } }