1. 程式人生 > >go 語言版 B + 樹

go 語言版 B + 樹

在搞的專案需要實現一顆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++
		}
	}