1
0

Golangで簡易的なBTreeを実装してみる

Posted at

はじめに

Golang初心者ですが、RDBを書くために始めに言語仕様の確認も踏まえ
簡易的なBTree(2分木)のコードを書いてみたので備忘録として投稿します。
linux環境下で、goをダウンロード・環境構築している条件下で、
『go run (ファイル名).go』とファイルが置いてあるDirで打つことで実行できます。
なお、表示に使う関数群はGPT4に補間してもらいました。
最近精度が大幅に向上していてすごいです。一番下に参考先の記事のリンクを張らせて頂きます。
詳しく説明を付することもできますが、個人的な予定の時間の関係でコードのみの公開です。

【以下コード】
package main

import (
	"fmt"
	"math"
	"strings"
)

type Item interface {
	Eq(Item) bool
	Less(Item) bool
}

type Int int

func (n Int) Eq(m Item) bool {
	return m != nil && n == m.(Int)
}

func (n Int) Less(m Item) bool {
	return m != nil && n < m.(Int)
}

type Node struct {
	item        Item
	left, right *Node
}

func newNode(x Item) *Node {
	return &Node{item: x}
}

type Tree struct {
	root *Node
}

func newTree() *Tree {
	return &Tree{}
}

func (t *Tree) insertTree(x Item) {
	t.root = insertNode(t.root, x)
}

func insertNode(node *Node, x Item) *Node {
	if node == nil {
		return newNode(x)
	}
	if x.Less(node.item) {
		node.left = insertNode(node.left, x)
	} else if x.Eq(node.item) {
		// 追加したい値xが既に木のノードが保持する値の一つとして存在した場合の処理
        // 今回は一意性を保証するためなにも記述しない、必要に応じて変更
	} else {
		node.right = insertNode(node.right, x)
	}
	return node
}

func (t *Tree) searchTree(x Item) bool {
	return searchNode(t.root, x)
}

func searchNode(node *Node, x Item) bool {
	if node == nil {
		return false
	}
	if x.Eq(node.item) {
		return true
	}
	if x.Less(node.item) {
		return searchNode(node.left, x)
	}
	return searchNode(node.right, x)
}

func (t *Tree) deleteTree(x Item) {
	t.root = deleteNode(t.root, x)
}

func deleteNode(node *Node, x Item) *Node {
    // 削除したいノードが子ノードなしの場合
	if node == nil {
		return nil
	}
    //削除したいノードの子ノード=1
	if x.Eq(node.item) {
		if node.left == nil {
			return node.right
		} else if node.right == nil {
			return node.left
		}
    	// 削除したい子ノード=2(最大)
        // 削除したいノードの右側の子分木で最小の値を保持するノードの値をnode.itemに格納
		node.item = minValue(node.right)
		// node.itemに格納した値があるノードを削除(事実上の前のnodeの削除+新しいノードを
        // node位置へ移動)
		node.right = deleteNode(node.right, node.item)
	} else if x.Less(node.item) {
		node.left = deleteNode(node.left, x)
	} else {
		node.right = deleteNode(node.right, x)
	}
	return node
}

func minValue(node *Node) Item {
	current := node
	for current.left != nil {
		current = current.left
	}
	return current.item
}

// ツリーの深さを計算する関数
func treeDepth(node *Node) int {
	if node == nil {
		return 0
	}
	leftDepth := treeDepth(node.left)
	rightDepth := treeDepth(node.right)
	if leftDepth > rightDepth {
		return leftDepth + 1
	}
	return rightDepth + 1
}

// 特定の深さのノードを表示する関数
func printLevel(node *Node, level int, indentSize int) string {
	if node == nil {
		return strings.Repeat(" ", indentSize) // 空のスペースで埋める
	}
	if level == 1 {
		return fmt.Sprintf("%s%d%s", strings.Repeat(" ", indentSize/2), node.item, strings.Repeat(" ", indentSize/2))
	} else if level > 1 {
		left := printLevel(node.left, level-1, indentSize/2)
		right := printLevel(node.right, level-1, indentSize/2)
		return left + right
	}
	return ""
}

// ツリー全体を表示する関数
func (t *Tree) printTree() {
	depth := treeDepth(t.root)
	indentSize := int(math.Pow(2, float64(depth))) // インデントのサイズ
	for i := 1; i <= depth; i++ {
		fmt.Println(printLevel(t.root, i, indentSize))
	}
}

// // ツリーを視覚的に表示する関数
// func printNode(node *Node, prefix string, isLeft bool) {
// 	if node != nil {
// 			fmt.Println(prefix + getDirection(isLeft) + fmt.Sprint(node.item))
// 			newPrefix := prefix + getBranch(isLeft)
// 			printNode(node.left, newPrefix, true)
// 			printNode(node.right, newPrefix, false)
// 	}
// }

// func getDirection(isLeft bool) string {
// 	if isLeft {
// 			return "├── "
// 	}
// 	return "└── "
// }

// func getBranch(isLeft bool) string {
// 	if isLeft {
// 			return "│   "
// 	}
// 	return "    "
// }

// // ツリー全体を表示する関数
//
//	func (t *Tree) printTree() {
//		printNode(t.root, "", false)
//	}
func main() {
	a := newTree()
	for _, v := range []int{5, 6, 4, 7, 3, 8, 2, 9, 1} {
		a.insertTree(Int(v))
	}
	a.printTree()

	fmt.Println("Searching for 6:", a.searchTree(Int(6)))
	fmt.Println("Searching for 10:", a.searchTree(Int(10)))

	a.deleteTree(Int(5))
	a.printTree()
}

【その他】
参照先:https://qiita.com/oko1977/items/822c0b3168716ebfbf0c
見て勉強させて頂きました。
insertNode関数のreturn nodeの部分がなぜ必要なのかということについて最初は少し納得がいかなかったです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0