B-tree

爱跑步的乌龟發表於2024-07-20
package main

import (
    "fmt"
    "math/rand"
    "time"
)

//B樹的節點
type BtreeNode struct {
    Leaf     bool         //是否葉子
    N        int          //分支的數量
    keys     []int        //儲存資料
    Children []*BtreeNode //指向自己的多個分支節點

}

//新建一個節點
func NewBtreeNode(n int, branch int, leaf bool) *BtreeNode {
    return &BtreeNode{leaf,
        n,
        make([]int, branch*2-1), //n個brach對應2n,root  2n-1
        make([]*BtreeNode, branch*2)}
}

//搜尋B樹枝的節點
func (btreenode *BtreeNode) Search(key int) (mynode *BtreeNode, idx int) {
    i := 0
    //找到合適的位置,找到最後一個小於key的,i之後的就是大於等於
    for i < btreenode.N && btreenode.keys[i] < key {
        i += 1
    }
    if i < btreenode.N && btreenode.keys[i] == key {
        mynode, idx = btreenode, i //找到
    } else if btreenode.Leaf == false {
        //進入孩子葉子繼續搜尋
        mynode, idx = btreenode.Children[i].Search(key)
    }
    return
}
func (parent *BtreeNode) Split(branch int, idx int) {
    full := parent.Children[idx]                         //孩子節點
    newnode := NewBtreeNode(branch-1, branch, full.Leaf) //新建一個節點,備份
    for i := 0; i < branch-1; i++ {
        newnode.keys[i] = full.keys[i+branch] //資料移動,跳過一個分支
        newnode.Children[i] = full.Children[i+branch]
    }
    newnode.Children[branch-1] = full.Children[branch*2-1] //處理最後
    full.N = branch - 1
    //x新增一個key到children
    for i := parent.N; i > idx; i-- {
        parent.Children[i] = parent.Children[i-1]
        parent.keys[i+1] = parent.keys[i] //從後往前移動
    }
    parent.keys[idx] = full.keys[branch-1]
    parent.Children[idx+1] = newnode //插入資料,增加總量
    parent.N++

}

//節點插入資料
func (btreenode *BtreeNode) InsertNonFull(branch int, key int) {
    if btreenode == nil {
        return
    }
    i := btreenode.N    //記錄葉子節點的總量
    if btreenode.Leaf { //是葉子或者不是
        for i > 0 && key < btreenode.keys[i-1] {
            btreenode.keys[i] = btreenode.keys[i-1] //從後往前移動,
            i--                                     //i從後往前移動
        }
        btreenode.keys[i] = key //插入資料
        btreenode.N++           //總量加一

    } else {
        for i > 0 && key < btreenode.keys[i-1] {
            i-- //i從後往前移動
        }
        c := btreenode.Children[i] //找到下標
        if c != nil && c.N == 2*branch-1 {
            btreenode.Split(branch, i) //切割
            if key > btreenode.keys[i] {
                i++
            }
        }
        btreenode.Children[i].InsertNonFull(branch, key) //遞迴插入孩子葉子

    }

}

//節點顯示為字串
func (btreenode *BtreeNode) String() string {
    return fmt.Sprintf("{n=%d,leaf=%v,Children=%v}\n", btreenode.N, btreenode.keys, btreenode.Children)
}

//B樹
type Btree struct {
    Root   *BtreeNode //根節點
    branch int        //分支的數量
}

//插入
func (tree *Btree) Insert(key int) {
    root := tree.Root //根節點
    if root.N == 2*tree.branch-1 {
        s := NewBtreeNode(0, tree.branch, false)
        tree.Root = s //新建一個節點備份根節點
        s.Children[0] = root
        s.Split(tree.branch, 0) //拆分整合
        root.InsertNonFull(tree.branch, key)
    } else {
        root.InsertNonFull(tree.branch, key)
    }
}

//查詢
func (tree *Btree) Search(key int) (n *BtreeNode, idx int) {
    return tree.Root.Search(key)
}

//返回字串
func (tree *Btree) String() string {

    return tree.Root.String() //返回樹的字串
}

//新建B樹
func NewBtree(branch int) *Btree {
    return &Btree{NewBtreeNode(0, branch, true), branch}
}

func main() {
    mybtree := NewBtree(100000)
    for i := 100000; i > 0; i-- {
        mybtree.Insert(rand.Int() % 100000)
    }
    fmt.Println(mybtree.String())
    for i := 0; i < 10000; i++ {
        starttime := time.Now()
        fmt.Println(mybtree.Search(i))
        fmt.Println("一共用了", time.Since(starttime))
    }

}