B树的思路以及go语言实现【算法】
一、定义
B树B-tree,B-树其实就是B树,英文名balanced tree。一棵m阶B树(balanced tree of order m)是一棵平衡的m路搜索树。它或者是空树,或者是满足下列性质的树:
1、根结点至少有两个子女;
2、每个非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m - 1;
3、除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:┌m/2┐ <= k <= m ;
4、所有的叶子结点都位于同一层。
5、B树是搜索树(有序)
二、思路实现
B树设计初衷就是为了查找,所以查找是最简单的,相反插入、删除反而异常繁琐,每次操作都会多次使用查找。这里我这要分析B树中如何插入及删除,。
插入操作,一般搜索树而言,插入就是找到相应的位置,直接加入节点,B树为了数据能够快速被查找,在插入的时候不仅要插入到相应的位置,还需要根据情况来调整树的高度以及宽度,尽可能使树高度下降。
插入分为以下几种:
1、若插入的节点中含有的关键词小于m-1,则正常添加关键词到节点即可
2、若插入的节点中含有关键词等于m-1(这里不存在大于m-1,因为一旦达到m就会分裂),则需要向上分裂(这个是需要递归到根节点)
//r为根节点指针,key为插入关键词,value是插入的数据 func InsertTreeNode(r *node, key int16, value int16) int16 { if r.indexesNum == 0 { // 根节点为空 fmt.Println("root is empty node", r) r.indexes = make([]int16, M+1, M+1) r.vals = make([]int16, M+1, M+1) r.children = make([]*node, M+1, M+1) var i int16 for i = 0; i <= M; i++ { r.indexes[0] = 0 r.vals[i] = 0 r.children[i] = nil } r.indexesNum++ r.indexes[1] = key r.vals[1] = value } else { // Find out if this node exists res, pos, state := FindOfNode(r, key)//res是查找到的节点,pos找到插入的位置 if state == TRUE {// 每次查找都需要判断是否存在数据 fmt.Println("This n node is exist!") return ERROR } if state == FALSE {// 不存在关键词,表示可以插入 res.indexesNum++ var i int16 for i = res.indexesNum; i > pos; i-- { res.indexes[i] = res.indexes[i-1] res.children[i] = res.children[i-1] res.vals[i] = res.vals[i-1] } res.indexes[pos] = key res.vals[pos] = value res.children[pos] = nil if res.indexesNum == M {//这里就是达到节点能储存的关键词上线就需要向上分裂 splitEdge(res) } renewParent(r) //每次分裂会导致节点位置变化,需要重新指向父级指针 } } return OK }
/** Split Nodes
向上分裂 */ func splitEdge(n *node) int16 { var n1, n2 *node var i, pos int16 if n.parent == nil { n1 = MakeNode() n2 = MakeNode() n1.indexesNum = M / 2 n2.indexesNum = M - M/2 - 1 n1.parent = n n2.parent = n for i = 0; i <= M; i++ { n1.children[i] = nil n1.indexes[i] = 0 n1.vals[i] = 0 n2.children[i] = nil n2.indexes[i] = 0 n2.vals[i] = 0 } for i = 0; i <= M/2; i++ { n1.children[i] = n.children[i] n1.indexes[i] = n.indexes[i] n1.vals[i] = n.vals[i] } n2.children[0] = n.children[M/2+1] for i = M/2 + 2; i <= M; i++ { n2.children[i-M/2-1] = n.children[i] n2.indexes[i-M/2-1] = n.indexes[i] n2.vals[i] = n.vals[i] } n.indexesNum = 1 n.children[0] = n1 n.children[1] = n2 n.indexes[1] = n.indexes[M/2+1] n.vals[1] = n.vals[M/2+1] for i = 2; i <= M; i++ { n.indexes[i] = 0 n.vals[i] = 0 n.children[i] = nil } } else { pos = WhichChildNode(n) for i = n.parent.indexesNum; i > pos; i-- { n.parent.indexes[i+1] = n.parent.indexes[i] n.parent.vals[i+1] = n.parent.vals[i] n.parent.children[i+1] = n.parent.children[i] } n.parent.indexesNum++ n.parent.indexes[pos+1] = n.indexes[M/2+1] n.parent.vals[pos+1] = n.vals[M/2+1] n2 = MakeNode() n.parent.children[pos+1] = n2 for i = 0; i <= M; i++ { n2.indexes[i] = 0 n2.vals[i] = 0 n2.children[i] = nil } n2.indexesNum = M - M/2 - 1 n2.parent = n.parent n2.children[0] = n.children[M/2+1] for i = M/2 + 2; i <= M; i++ { // Initialization n2 n2.indexes[i-M/2-1] = n.indexes[i] n2.vals[i-M/2-1] = n.vals[i] n2.children[i-M/2-1] = n.children[i] } n.indexesNum = M / 2 for i = M/2 + 1; i <= M; i++ { // Initialization n1 n.indexes[i] = 0 n.vals[i] = 0 n.children[i] = nil } if n.parent.indexesNum == M { splitEdge(n.parent) } } return OK }
上面插入还算简单,接着就是删除操作,对于删除方式,首先分为叶子跟非叶子节点:
非叶子节点:如果该关键字所在的结点不是最下层的非叶子结点,则先需要把此关键字与它在B树中后继(最近右边子树)对换位置,即以指针Pi所指子树中的最小关键字Y代替Ki,然后在相应的结点中删除Y。
叶子节点:(非叶子节点需要替换到叶子节点,然后在进行下面的操作)
1、若该关键字Ki所在结点中的关键字个数大于等于m/2,则可以直接从该结点中删除该关键字和相应指针即可。(这个最简单)
2、若该关键字Ki所在结点中的关键字个数小于m/2,可以分为以下几种:
1)考虑左兄弟节点中的关键词个数,是否大于等于m/2(这个不需要递归到根节点,因为节点高度没变化)
2)考虑右兄弟节点中的关键词个数,是否大于等于m/2(这个不需要递归到根节点,因为节点高度没变化)
3)兄弟都没多余的关键词个数,那就合并(这个需要递归到根节点)
① 左兄弟(存在的情况)+ 自身(删除后剩余的关键词)+与父级节点中的一个关键词 = 合并到左兄弟节点中
② 右兄弟(存在的情况)+ 自身(删除后剩余的关键词)+与父级节点中的一个关键词 = 合并到自身节点中
//r为根节点,key为删除的关键词 func DeleteTreeNode(r *node, key int16) int16 { if r == nil { return ERROR } // 找出存在的关键词 res, pos, state := FindOfNode(r, key) if state == FALSE { return FALSE } if state == TRUE { if res.children[0] == nil { // 如果为叶子节点 for i := pos; i < res.indexesNum; i++ { res.indexes[i] = res.indexes[i+1] res.vals[i] = res.vals[i+1] res.children[i] = res.children[i+1] } res.indexesNum-- MergeTreeNode(res) } else {//非叶子节点需要替换后继子节点中最小叶子节点中的最小关键词 rn := FindMinKey(res, res.children[pos], pos) var i int16 for i = 1; i < rn.indexesNum; i++ { rn.indexes[i] = rn.indexes[i+1] rn.vals[i] = rn.vals[i+1] rn.children[i] = rn.children[i+1] } rn.indexesNum-- MergeTreeNode(rn) } renewParent(r) } return ERROR }
/** 合并操作 */ func MergeTreeNode(n *node) int16 { p := n.parent if p == nil || p.indexesNum <= 0 { if n.indexesNum <= 0 { if n.children[0] != nil { root = *n.children[0] } } else { root = *n } root.parent = nil return TRUE } if n.indexesNum >= M/2 { return TRUE } // Borrow it from your brother pos := WhichChildNode(n) // Borrow from brother left var i, j int16 for i = pos - 1; i >= 0; i-- { if p.children[i] != nil && p.children[i].indexesNum > M/2 { // 左兄弟节点有多的关键词 for j = i; j < pos; j++ { MoveLeftToRight(p.children[j], p.children[j+1], p, j+1) } return TRUE } } // Borrow from brother right for i = pos + 1; i <= p.indexesNum; i++ { if p.children[i] != nil && p.children[i].indexesNum > M/2 {// 右兄弟节点有多关键词 for j = i; j > pos; j-- { MoveRightToLeft(p.children[j], p.children[j-1], p, j) } return TRUE } } // 兄弟节点都没多,只能合并 if pos > 0 {// 我向左兄弟合并 if p.children[pos] == nil || p.children[pos-1] == nil { return ERROR } MergeParentBotherLeft(p.children[pos], p.children[pos-1], p, pos) MergeTreeNode(p)// 递归操作 } else if pos < p.indexesNum { // 右兄弟向我合并 if p.children[pos] == nil || p.children[pos+1] == nil { return ERROR } MergeParentBotherRight(p.children[pos+1], p.children[pos], p, pos+1) MergeTreeNode(p) } return TRUE }
三、总结
起初只是以为B树是简单搜索树的一种,本人也是经常刷水题的程序猿,然而却花费了近一周时间才做出来(我也不知道有没有问题,测试3到10阶),最难的是删除合并,这个思路太卡了,借鉴了各种大神图文讲解,代码研究,终于写出来了。后面有空还会研究B+树、B*树以及其他算法结构。下面附上完整代码。
package gobbk import "fmt" const ( // status NOFIND int16 = -1 OK int16 = 0 TRUE int16 = 1 FALSE int16 = 2 ERROR int16 = 6 // Balanced tree of order M M int16 = 4 nodeMinM = M/2 - 1 nodeMaxM = M - 1 ) type ( node struct { // Number of indexes indexesNum int16 // Do not assign zero indexes []int16 // Custom data values vals []int16 children []*node parent *node } ) // Set a root node var root node func GetRoot() node { return root } // Make a new node func MakeNode() *node { n := node{ indexesNum: 1, indexes: make([]int16, M+1), vals: make([]int16, M+1), children: make([]*node, M+1), parent: nil, } return &n } // Delete this node for the Balanced Tree Node func DeleteTreeNode(r *node, key int16) int16 { if r == nil { return ERROR } // Find out if this node exists res, pos, state := FindOfNode(r, key) if state == FALSE { return FALSE } if state == TRUE { if res.children[0] == nil { // If it's a leaf node for i := pos; i < res.indexesNum; i++ { res.indexes[i] = res.indexes[i+1] res.vals[i] = res.vals[i+1] res.children[i] = res.children[i+1] } res.indexesNum-- MergeTreeNode(res) } else { rn := FindMinKey(res, res.children[pos], pos) var i int16 for i = 1; i < rn.indexesNum; i++ { rn.indexes[i] = rn.indexes[i+1] rn.vals[i] = rn.vals[i+1] rn.children[i] = rn.children[i+1] } rn.indexesNum-- MergeTreeNode(rn) } renewParent(r) } return ERROR } func FindMinKey(n *node, rn *node, pos int16) *node { if n == nil || rn == nil { return nil } if rn.children[0] == nil || rn.children[0].indexesNum == 0 { n1 := MakeNode() n1.indexes[1] = n.indexes[pos] n1.vals[1] = n.vals[pos] n.indexes[pos] = rn.indexes[1] n.vals[pos] = rn.vals[1] rn.indexes[1] = n1.indexes[1] rn.vals[1] = n1.vals[1] return rn } return FindMinKey(n, rn.children[0], pos) } /** Merge Nodes */ func MergeTreeNode(n *node) int16 { p := n.parent if p == nil || p.indexesNum <= 0 { if n.indexesNum <= 0 { if n.children[0] != nil { root = *n.children[0] } } else { root = *n } root.parent = nil return TRUE } if n.indexesNum >= M/2 { return TRUE } // Borrow it from your brother pos := WhichChildNode(n) // Borrow from brother left var i, j int16 for i = pos - 1; i >= 0; i-- { if p.children[i] != nil && p.children[i].indexesNum > M/2 { // The left brother has many nodes for j = i; j < pos; j++ { MoveLeftToRight(p.children[j], p.children[j+1], p, j+1) } return TRUE } } // Borrow from brother right for i = pos + 1; i <= p.indexesNum; i++ { if p.children[i] != nil && p.children[i].indexesNum > M/2 { for j = i; j > pos; j-- { MoveRightToLeft(p.children[j], p.children[j-1], p, j) } return TRUE } } // Borrow node from parent node if pos > 0 { if p.children[pos] == nil || p.children[pos-1] == nil { return ERROR } MergeParentBotherLeft(p.children[pos], p.children[pos-1], p, pos) MergeTreeNode(p) } else if pos < p.indexesNum { if p.children[pos] == nil || p.children[pos+1] == nil { return ERROR } MergeParentBotherRight(p.children[pos+1], p.children[pos], p, pos+1) MergeTreeNode(p) } return TRUE } func MergeParentBotherRight(a *node, b *node, p *node, pos int16) { var i int16 /*------*/ b.indexesNum++ b.indexes[b.indexesNum] = p.indexes[pos] b.vals[b.indexesNum] = p.vals[pos] b.children[b.indexesNum] = a.children[0] /*------*/ for i = pos; i < p.indexesNum; i++ { p.indexes[i] = p.indexes[i+1] p.vals[i] = p.vals[i+1] p.children[i] = p.children[i+1] } p.indexesNum-- /*------*/ for i = 1; i <= a.indexesNum; i++ { b.indexesNum++ b.indexes[b.indexesNum] = a.indexes[i] b.vals[b.indexesNum] = a.vals[i] b.children[b.indexesNum] = a.children[i] } a.indexesNum = 0 } func MergeParentBotherLeft(a *node, b *node, p *node, pos int16) { var i int16 /*------*/ b.indexesNum++ b.indexes[b.indexesNum] = p.indexes[pos] b.vals[b.indexesNum] = p.vals[pos] b.children[b.indexesNum] = a.children[0] /*------*/ for i = pos; i < p.indexesNum; i++ { p.indexes[i] = p.indexes[i+1] p.vals[i] = p.vals[i+1] p.children[i] = p.children[i+1] } p.indexesNum-- /*------*/ for i = 1; i <= a.indexesNum; i++ { b.indexesNum++ b.indexes[b.indexesNum] = a.indexes[i] b.vals[b.indexesNum] = a.vals[i] b.children[b.indexesNum] = a.children[i] } a.indexesNum = 0 } func MoveRightToLeft(a *node, b *node, p *node, pos int16) { var i int16 /*------*/ p.indexesNum++ p.children[p.indexesNum] = p.children[p.indexesNum-1] for i = p.indexesNum; i > pos+1; i-- { p.indexes[i] = p.indexes[i-1] p.vals[i] = p.vals[i-1] p.children[i-1] = p.children[i-2] } /*------*/ p.indexes[pos+1] = a.indexes[1] p.vals[pos+1] = a.vals[1] p.children[pos] = a.children[0] for i = 1; i < a.indexesNum; i++ { a.indexes[i] = a.indexes[i+1] a.vals[i] = a.vals[i+1] a.children[i-1] = a.children[i] } a.children[a.indexesNum-1] = a.children[a.indexesNum] a.indexesNum-- /*------*/ b.indexesNum++ b.indexes[b.indexesNum] = p.indexes[pos] b.vals[b.indexesNum] = p.vals[pos] b.children[b.indexesNum] = p.children[pos] /*------*/ for i = pos; i < p.indexesNum; i++ { p.indexes[i] = p.indexes[i+1] p.vals[i] = p.vals[i+1] p.children[i] = p.children[i+1] } p.indexesNum-- } func MoveLeftToRight(a *node, b *node, p *node, pos int16) { var i int16 /*------*/ p.indexesNum++ for i = p.indexesNum; i > pos; i-- { p.indexes[i] = p.indexes[i-1] p.vals[i] = p.vals[i-1] p.children[i] = p.children[i-1] } /*------*/ p.indexes[pos] = a.indexes[a.indexesNum] p.vals[pos] = a.vals[a.indexesNum] p.children[pos] = a.children[a.indexesNum] a.indexesNum-- /*------*/ b.indexesNum++ for i = b.indexesNum; i > 0; i-- { b.indexes[i] = b.indexes[i-1] b.vals[i] = b.vals[i-1] b.children[i] = b.children[i-1] } b.indexes[1] = p.indexes[pos+1] b.vals[1] = p.vals[pos+1] b.children[0] = p.children[pos] /*------*/ p.children[pos] = p.children[pos+1] for i = pos + 1; i < p.indexesNum; i++ { p.indexes[i] = p.indexes[i+1] p.vals[i] = p.vals[i+1] p.children[i] = p.children[i+1] } p.indexesNum-- } // r为根节点指针,key为插入关键词,value是插入的数据 返回state状态 // Insert a new node for the Balanced Tree Node func InsertTreeNode(r *node, key int16, value int16) int16 { if r.indexesNum == 0 { // The root node is empty fmt.Println("root is empty node", r) r.indexes = make([]int16, M+1, M+1) r.vals = make([]int16, M+1, M+1) r.children = make([]*node, M+1, M+1) var i int16 for i = 0; i <= M; i++ { r.indexes[0] = 0 r.vals[i] = 0 r.children[i] = nil } r.indexesNum++ r.indexes[1] = key r.vals[1] = value } else { // Find out if this node exists res, pos, state := FindOfNode(r, key) if state == TRUE { fmt.Println("This n node is exist!") return ERROR } if state == FALSE { res.indexesNum++ var i int16 for i = res.indexesNum; i > pos; i-- { res.indexes[i] = res.indexes[i-1] res.children[i] = res.children[i-1] res.vals[i] = res.vals[i-1] } res.indexes[pos] = key res.vals[pos] = value res.children[pos] = nil if res.indexesNum == M { splitEdge(res) } renewParent(r) } } return OK } /** Split Nodes */ func splitEdge(n *node) int16 { var n1, n2 *node var i, pos int16 if n.parent == nil { n1 = MakeNode() n2 = MakeNode() n1.indexesNum = M / 2 n2.indexesNum = M - M/2 - 1 n1.parent = n n2.parent = n for i = 0; i <= M; i++ { n1.children[i] = nil n1.indexes[i] = 0 n1.vals[i] = 0 n2.children[i] = nil n2.indexes[i] = 0 n2.vals[i] = 0 } for i = 0; i <= M/2; i++ { n1.children[i] = n.children[i] n1.indexes[i] = n.indexes[i] n1.vals[i] = n.vals[i] } n2.children[0] = n.children[M/2+1] for i = M/2 + 2; i <= M; i++ { n2.children[i-M/2-1] = n.children[i] n2.indexes[i-M/2-1] = n.indexes[i] n2.vals[i] = n.vals[i] } n.indexesNum = 1 n.children[0] = n1 n.children[1] = n2 n.indexes[1] = n.indexes[M/2+1] n.vals[1] = n.vals[M/2+1] for i = 2; i <= M; i++ { n.indexes[i] = 0 n.vals[i] = 0 n.children[i] = nil } } else { pos = WhichChildNode(n) for i = n.parent.indexesNum; i > pos; i-- { n.parent.indexes[i+1] = n.parent.indexes[i] n.parent.vals[i+1] = n.parent.vals[i] n.parent.children[i+1] = n.parent.children[i] } n.parent.indexesNum++ n.parent.indexes[pos+1] = n.indexes[M/2+1] n.parent.vals[pos+1] = n.vals[M/2+1] n2 = MakeNode() n.parent.children[pos+1] = n2 for i = 0; i <= M; i++ { n2.indexes[i] = 0 n2.vals[i] = 0 n2.children[i] = nil } n2.indexesNum = M - M/2 - 1 n2.parent = n.parent n2.children[0] = n.children[M/2+1] for i = M/2 + 2; i <= M; i++ { // Initialization n2 n2.indexes[i-M/2-1] = n.indexes[i] n2.vals[i-M/2-1] = n.vals[i] n2.children[i-M/2-1] = n.children[i] } n.indexesNum = M / 2 for i = M/2 + 1; i <= M; i++ { // Initialization n1 n.indexes[i] = 0 n.vals[i] = 0 n.children[i] = nil } if n.parent.indexesNum == M { splitEdge(n.parent) } } return OK } // Renew Parent func renewParent(n *node) int16 { if n == nil { return ERROR } var i int16 for i = 0; i <= n.indexesNum; i++ { if n.children[i] != nil { n.children[i].parent = n renewParent(n.children[i]) } } return TRUE } // Find the number of child nodes it is in func WhichChildNode(n *node) int16 { if n == nil { return 0 } var i int16 for i = 0; i <= n.parent.indexesNum; i++ { if n.parent.children[i] == n { return i } } return 0 } /** Find this position of this node @param r (This is root node) @param k (This is the node keyword to be found) @return res (This is the node found) @return pos (This is the node position found) @return state */ func FindOfNode(r *node, k int16) (res *node, pos int16, state int16) { pos = 0 if r == nil { return res, pos, FALSE } q := r for q != nil { var i int16 = 1 key := q.indexes[i] for i <= q.indexesNum { if k == key { // Find this n node res = q pos = i return res, pos, TRUE } if k > key { if i == q.indexesNum { if q.children[i] == nil { res = q pos = i + 1 return res, pos, FALSE } q = q.children[i] break } i++ key = q.indexes[i] continue } if k < key { if q.children[i-1] == nil { res = q pos = i return res, pos, FALSE } q = q.children[i-1] break } } } return res, pos, ERROR } // Traversing tree nodes in middle order func PrintTreeNode(n *node) { arr := []*node{n} iarr := []int16{1} var preH int16 = 1 index := 0 for index < len(arr) && arr[index] != nil { if iarr[index] != preH { preH = iarr[index] fmt.Println("") } fmt.Print("[") var j int16 for j = 1; j <= arr[index].indexesNum; j++ { fmt.Print(" ", arr[index].indexes[j], " ") } fmt.Print("]") for j = 0; j <= arr[index].indexesNum; j++ { if arr[index].children[j] == nil { break } arr = append(arr, arr[index].children[j]) iarr = append(iarr, preH+1) } index++ } fmt.Println("") return } func InsertTest() { //40, 80, 30, 50, 90, 25, 35, 44, 46, 55, 82, 85, 93 arr := []int16{50, 30, 80, 20, 40, 60, 90, 10, 25, 35, 44, 55, 85, 93, 82, 46,50} for i := 0; i < len(arr); i++ { InsertTreeNode(&root, arr[i], arr[i]) fmt.Println(" i:", i, ",insert:", arr[i]) PrintTreeNode(&root) } } func DeleteTest() { arr := []int16{50, 30, 80, 20, 40, 60, 90, 10, 25, 35, 44, 55, 85, 93, 82, 46} for i := 0; i < len(arr); i++ { DeleteTreeNode(&root, arr[i]) fmt.Println(" i:", i, ",delete:", arr[i]) PrintTreeNode(&root) } }