Re-translate the Japanese version (#1871)

* Retranslate Japanese docs with GPT-5.4

* Retranslate Japanese code with GPT-5.4
This commit is contained in:
Yudong Jin
2026-03-30 07:30:15 +08:00
committed by GitHub
parent fe6443235b
commit d7b2277d2b
1444 changed files with 83312 additions and 8363 deletions
@@ -0,0 +1,101 @@
// File: array_binary_tree.go
// Created Time: 2023-07-24
// Author: Reanon (793584285@qq.com)
package chapter_tree
/* 配列表現による二分木クラス */
type arrayBinaryTree struct {
tree []any
}
/* コンストラクタ */
func newArrayBinaryTree(arr []any) *arrayBinaryTree {
return &arrayBinaryTree{
tree: arr,
}
}
/* リスト容量 */
func (abt *arrayBinaryTree) size() int {
return len(abt.tree)
}
/* インデックス i のノードの値を取得 */
func (abt *arrayBinaryTree) val(i int) any {
// インデックスが範囲外なら、空きを表す null を返す
if i < 0 || i >= abt.size() {
return nil
}
return abt.tree[i]
}
/* インデックス i のノードの左子ノードのインデックスを取得 */
func (abt *arrayBinaryTree) left(i int) int {
return 2*i + 1
}
/* インデックス i のノードの右子ノードのインデックスを取得 */
func (abt *arrayBinaryTree) right(i int) int {
return 2*i + 2
}
/* インデックス i のノードの親ノードのインデックスを取得 */
func (abt *arrayBinaryTree) parent(i int) int {
return (i - 1) / 2
}
/* レベル順走査 */
func (abt *arrayBinaryTree) levelOrder() []any {
var res []any
// 配列を直接走査する
for i := 0; i < abt.size(); i++ {
if abt.val(i) != nil {
res = append(res, abt.val(i))
}
}
return res
}
/* 深さ優先探索 */
func (abt *arrayBinaryTree) dfs(i int, order string, res *[]any) {
// 空きスロットなら返す
if abt.val(i) == nil {
return
}
// 先行順走査
if order == "pre" {
*res = append(*res, abt.val(i))
}
abt.dfs(abt.left(i), order, res)
// 中順走査
if order == "in" {
*res = append(*res, abt.val(i))
}
abt.dfs(abt.right(i), order, res)
// 後順走査
if order == "post" {
*res = append(*res, abt.val(i))
}
}
/* 先行順走査 */
func (abt *arrayBinaryTree) preOrder() []any {
var res []any
abt.dfs(0, "pre", &res)
return res
}
/* 中順走査 */
func (abt *arrayBinaryTree) inOrder() []any {
var res []any
abt.dfs(0, "in", &res)
return res
}
/* 後順走査 */
func (abt *arrayBinaryTree) postOrder() []any {
var res []any
abt.dfs(0, "post", &res)
return res
}
@@ -0,0 +1,47 @@
// File: array_binary_tree_test.go
// Created Time: 2023-07-24
// Author: Reanon (793584285@qq.com)
package chapter_tree
import (
"fmt"
"testing"
. "github.com/krahets/hello-algo/pkg"
)
func TestArrayBinaryTree(t *testing.T) {
// 二分木を初期化
// ここでは、配列から直接二分木を生成する関数を利用する
arr := []any{1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15}
root := SliceToTree(arr)
fmt.Println("\n二分木を初期化")
fmt.Println("二分木の配列表現:")
fmt.Println(arr)
fmt.Println("二分木の連結リスト表現:")
PrintTree(root)
// 配列表現による二分木クラス
abt := newArrayBinaryTree(arr)
// ノードにアクセス
i := 1
l := abt.left(i)
r := abt.right(i)
p := abt.parent(i)
fmt.Println("\n現在のノードのインデックスは", i, ",値は", abt.val(i))
fmt.Println("左の子ノードのインデックスは", l, ",値は", abt.val(l))
fmt.Println("右の子ノードのインデックスは", r, ",値は", abt.val(r))
fmt.Println("親ノードのインデックスは", p, ",値は", abt.val(p))
// 木を走査
res := abt.levelOrder()
fmt.Println("\nレベル順走査:", res)
res = abt.preOrder()
fmt.Println("前順走査:", res)
res = abt.inOrder()
fmt.Println("中順走査:", res)
res = abt.postOrder()
fmt.Println("後順走査:", res)
}
+200
View File
@@ -0,0 +1,200 @@
// File: avl_tree.go
// Created Time: 2023-01-08
// Author: Reanon (793584285@qq.com)
package chapter_tree
import . "github.com/krahets/hello-algo/pkg"
/* AVL 木 */
type aVLTree struct {
// 根ノード
root *TreeNode
}
func newAVLTree() *aVLTree {
return &aVLTree{root: nil}
}
/* ノードの高さを取得 */
func (t *aVLTree) height(node *TreeNode) int {
// 空ノードの高さは -1、葉ノードの高さは 0
if node != nil {
return node.Height
}
return -1
}
/* ノードの高さを更新する */
func (t *aVLTree) updateHeight(node *TreeNode) {
lh := t.height(node.Left)
rh := t.height(node.Right)
// ノードの高さは最も高い部分木の高さ + 1 に等しい
if lh > rh {
node.Height = lh + 1
} else {
node.Height = rh + 1
}
}
/* 平衡係数を取得 */
func (t *aVLTree) balanceFactor(node *TreeNode) int {
// 空ノードの平衡係数は 0
if node == nil {
return 0
}
// ノードの平衡係数 = 左部分木の高さ - 右部分木の高さ
return t.height(node.Left) - t.height(node.Right)
}
/* 右回転 */
func (t *aVLTree) rightRotate(node *TreeNode) *TreeNode {
child := node.Left
grandChild := child.Right
// child を支点として node を右回転させる
child.Right = node
node.Left = grandChild
// ノードの高さを更新する
t.updateHeight(node)
t.updateHeight(child)
// 回転後の部分木の根ノードを返す
return child
}
/* 左回転 */
func (t *aVLTree) leftRotate(node *TreeNode) *TreeNode {
child := node.Right
grandChild := child.Left
// child を支点として node を左回転させる
child.Left = node
node.Right = grandChild
// ノードの高さを更新する
t.updateHeight(node)
t.updateHeight(child)
// 回転後の部分木の根ノードを返す
return child
}
/* 回転操作を行い、この部分木の平衡を回復する */
func (t *aVLTree) rotate(node *TreeNode) *TreeNode {
// ノード `node` の平衡係数を取得する
// Go では短い変数名が推奨されるため、ここで `bf` は `t.balanceFactor` を表す
bf := t.balanceFactor(node)
// 左に偏った木
if bf > 1 {
if t.balanceFactor(node.Left) >= 0 {
// 右回転
return t.rightRotate(node)
} else {
// 左回転してから右回転
node.Left = t.leftRotate(node.Left)
return t.rightRotate(node)
}
}
// 右に偏った木
if bf < -1 {
if t.balanceFactor(node.Right) <= 0 {
// 左回転
return t.leftRotate(node)
} else {
// 右回転してから左回転
node.Right = t.rightRotate(node.Right)
return t.leftRotate(node)
}
}
// 平衡木なので回転不要、そのまま返す
return node
}
/* ノードを挿入 */
func (t *aVLTree) insert(val int) {
t.root = t.insertHelper(t.root, val)
}
/* ノードを再帰的に挿入する(補助関数) */
func (t *aVLTree) insertHelper(node *TreeNode, val int) *TreeNode {
if node == nil {
return NewTreeNode(val)
}
/* 1. 挿入位置を探索してノードを挿入 */
if val < node.Val.(int) {
node.Left = t.insertHelper(node.Left, val)
} else if val > node.Val.(int) {
node.Right = t.insertHelper(node.Right, val)
} else {
// 重複ノードは挿入せず、そのまま返す
return node
}
// ノードの高さを更新する
t.updateHeight(node)
/* 2. 回転操作を行い、部分木の平衡を回復する */
node = t.rotate(node)
// 部分木の根ノードを返す
return node
}
/* ノードを削除 */
func (t *aVLTree) remove(val int) {
t.root = t.removeHelper(t.root, val)
}
/* ノードを再帰的に削除する(補助関数) */
func (t *aVLTree) removeHelper(node *TreeNode, val int) *TreeNode {
if node == nil {
return nil
}
/* 1. ノードを探索して削除 */
if val < node.Val.(int) {
node.Left = t.removeHelper(node.Left, val)
} else if val > node.Val.(int) {
node.Right = t.removeHelper(node.Right, val)
} else {
if node.Left == nil || node.Right == nil {
child := node.Left
if node.Right != nil {
child = node.Right
}
if child == nil {
// 子ノード数 = 0 の場合、node をそのまま削除して返す
return nil
} else {
// 子ノード数 = 1 の場合、node をそのまま削除する
node = child
}
} else {
// 子ノード数 = 2 の場合、中順走査の次のノードを削除し、そのノードで現在のノードを置き換える
temp := node.Right
for temp.Left != nil {
temp = temp.Left
}
node.Right = t.removeHelper(node.Right, temp.Val.(int))
node.Val = temp.Val
}
}
// ノードの高さを更新する
t.updateHeight(node)
/* 2. 回転操作を行い、部分木の平衡を回復する */
node = t.rotate(node)
// 部分木の根ノードを返す
return node
}
/* ノードを探索 */
func (t *aVLTree) search(val int) *TreeNode {
cur := t.root
// ループで探索し、葉ノードを越えたら抜ける
for cur != nil {
if cur.Val.(int) < val {
// 目標ノードは cur の右部分木にある
cur = cur.Right
} else if cur.Val.(int) > val {
// 目標ノードは cur の左部分木にある
cur = cur.Left
} else {
// 目標ノードが見つかったらループを抜ける
break
}
}
// 目標ノードを返す
return cur
}
+54
View File
@@ -0,0 +1,54 @@
// File: avl_tree_test.go
// Created Time: 2023-01-08
// Author: Reanon (793584285@qq.com)
package chapter_tree
import (
"fmt"
"testing"
. "github.com/krahets/hello-algo/pkg"
)
func TestAVLTree(t *testing.T) {
/* 空の AVL 木を初期化する */
tree := newAVLTree()
/* ノードを挿入 */
// ノード挿入後に AVL 木がどのように平衡を保つかに注目してほしい
testInsert(tree, 1)
testInsert(tree, 2)
testInsert(tree, 3)
testInsert(tree, 4)
testInsert(tree, 5)
testInsert(tree, 8)
testInsert(tree, 7)
testInsert(tree, 9)
testInsert(tree, 10)
testInsert(tree, 6)
/* 重複ノードを挿入する */
testInsert(tree, 7)
/* ノードを削除 */
// ノード削除後に AVL 木がどのように平衡を保つかに注目してほしい
testRemove(tree, 8) // 次数 0 のノードを削除する
testRemove(tree, 5) // 次数 1 のノードを削除する
testRemove(tree, 4) // 次数 2 のノードを削除する
/* ノードを検索 */
node := tree.search(7)
fmt.Printf("\n見つかったノードオブジェクトは %#v ,ノードの値 = %d \n", node, node.Val)
}
func testInsert(tree *aVLTree, val int) {
tree.insert(val)
fmt.Printf("\nノード %d を挿入後、AVL 木は \n", val)
PrintTree(tree.root)
}
func testRemove(tree *aVLTree, val int) {
tree.remove(val)
fmt.Printf("\nノード %d を削除後、AVL 木は \n", val)
PrintTree(tree.root)
}
@@ -0,0 +1,142 @@
// File: binary_search_tree.go
// Created Time: 2022-11-26
// Author: Reanon (793584285@qq.com)
package chapter_tree
import (
. "github.com/krahets/hello-algo/pkg"
)
type binarySearchTree struct {
root *TreeNode
}
func newBinarySearchTree() *binarySearchTree {
bst := &binarySearchTree{}
// 空の木を初期化する
bst.root = nil
return bst
}
/* 根ノードを取得 */
func (bst *binarySearchTree) getRoot() *TreeNode {
return bst.root
}
/* ノードを探索 */
func (bst *binarySearchTree) search(num int) *TreeNode {
node := bst.root
// ループで探索し、葉ノードを越えたら抜ける
for node != nil {
if node.Val.(int) < num {
// 目標ノードは cur の右部分木にある
node = node.Right
} else if node.Val.(int) > num {
// 目標ノードは cur の左部分木にある
node = node.Left
} else {
// 目標ノードが見つかったらループを抜ける
break
}
}
// 目標ノードを返す
return node
}
/* ノードを挿入 */
func (bst *binarySearchTree) insert(num int) {
cur := bst.root
// 木が空なら、根ノードを初期化する
if cur == nil {
bst.root = NewTreeNode(num)
return
}
// 挿入対象ノードの直前のノード位置
var pre *TreeNode = nil
// ループで探索し、葉ノードを越えたら抜ける
for cur != nil {
if cur.Val == num {
return
}
pre = cur
if cur.Val.(int) < num {
cur = cur.Right
} else {
cur = cur.Left
}
}
// ノードを挿入
node := NewTreeNode(num)
if pre.Val.(int) < num {
pre.Right = node
} else {
pre.Left = node
}
}
/* ノードを削除 */
func (bst *binarySearchTree) remove(num int) {
cur := bst.root
// 木が空なら、そのまま早期リターンする
if cur == nil {
return
}
// 削除対象ノードの直前のノード位置
var pre *TreeNode = nil
// ループで探索し、葉ノードを越えたら抜ける
for cur != nil {
if cur.Val == num {
break
}
pre = cur
if cur.Val.(int) < num {
// 削除対象ノードは右部分木にある
cur = cur.Right
} else {
// 削除対象ノードは左部分木にある
cur = cur.Left
}
}
// 削除対象ノードがなければそのまま返す
if cur == nil {
return
}
// 子ノード数は 0 または 1
if cur.Left == nil || cur.Right == nil {
var child *TreeNode = nil
// 削除対象ノードの子ノードを取り出す
if cur.Left != nil {
child = cur.Left
} else {
child = cur.Right
}
// ノード cur を削除する
if cur != bst.root {
if pre.Left == cur {
pre.Left = child
} else {
pre.Right = child
}
} else {
// 削除ノードが根ノードなら、根ノードを再設定
bst.root = child
}
// 子ノード数は 2
} else {
// 中順走査で削除対象ノード `cur` の次のノードを取得する
tmp := cur.Right
for tmp.Left != nil {
tmp = tmp.Left
}
// ノード tmp を再帰的に削除
bst.remove(tmp.Val.(int))
// tmp で cur を上書きする
cur.Val = tmp.Val
}
}
/* 二分探索木を出力 */
func (bst *binarySearchTree) print() {
PrintTree(bst.root)
}
@@ -0,0 +1,45 @@
// File: binary_search_tree_test.go
// Created Time: 2022-11-26
// Author: Reanon (793584285@qq.com)
package chapter_tree
import (
"fmt"
"testing"
)
func TestBinarySearchTree(t *testing.T) {
bst := newBinarySearchTree()
nums := []int{8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15}
// 注意:挿入順序が異なると異なる二分木が生成される。このシーケンスからは完全二分木を生成できる
for _, num := range nums {
bst.insert(num)
}
fmt.Println("\n初期化した二分木は:")
bst.print()
// 根ノードを取得
node := bst.getRoot()
fmt.Println("\n二分木の根ノードは:", node.Val)
// ノードを探索
node = bst.search(7)
fmt.Println("見つかったノードオブジェクトは", node, ",ノードの値 =", node.Val)
// ノードを挿入
bst.insert(16)
fmt.Println("\nノード 16 を挿入した後の二分木は:")
bst.print()
// ノードを削除
bst.remove(1)
fmt.Println("\nノード 1 を削除した後の二分木は:")
bst.print()
bst.remove(2)
fmt.Println("\nノード 2 を削除した後の二分木は:")
bst.print()
bst.remove(4)
fmt.Println("\nノード 4 を削除した後の二分木は:")
bst.print()
}
@@ -0,0 +1,35 @@
// File: binary_tree_bfs.go
// Created Time: 2022-11-26
// Author: Reanon (793584285@qq.com)
package chapter_tree
import (
"container/list"
. "github.com/krahets/hello-algo/pkg"
)
/* レベル順走査 */
func levelOrder(root *TreeNode) []any {
// キューを初期化し、ルートノードを追加する
queue := list.New()
queue.PushBack(root)
// 走査順を保存するためのスライスを初期化する
nums := make([]any, 0)
for queue.Len() > 0 {
// デキュー
node := queue.Remove(queue.Front()).(*TreeNode)
// ノードの値を保存する
nums = append(nums, node.Val)
if node.Left != nil {
// 左子ノードをキューに追加
queue.PushBack(node.Left)
}
if node.Right != nil {
// 右子ノードをキューに追加
queue.PushBack(node.Right)
}
}
return nums
}
@@ -0,0 +1,24 @@
// File: binary_tree_bfs_test.go
// Created Time: 2022-11-26
// Author: Reanon (793584285@qq.com)
package chapter_tree
import (
"fmt"
"testing"
. "github.com/krahets/hello-algo/pkg"
)
func TestLevelOrder(t *testing.T) {
/* 二分木を初期化 */
// ここでは、配列から直接二分木を生成する関数を利用する
root := SliceToTree([]any{1, 2, 3, 4, 5, 6, 7})
fmt.Println("\n二分木を初期化: ")
PrintTree(root)
// レベル順走査
nums := levelOrder(root)
fmt.Println("\nレベル順走査のノード出力シーケンス =", nums)
}
@@ -0,0 +1,44 @@
// File: binary_tree_dfs.go
// Created Time: 2022-11-26
// Author: Reanon (793584285@qq.com)
package chapter_tree
import (
. "github.com/krahets/hello-algo/pkg"
)
var nums []any
/* 先行順走査 */
func preOrder(node *TreeNode) {
if node == nil {
return
}
// 訪問順序:根ノード -> 左部分木 -> 右部分木
nums = append(nums, node.Val)
preOrder(node.Left)
preOrder(node.Right)
}
/* 中順走査 */
func inOrder(node *TreeNode) {
if node == nil {
return
}
// 訪問優先順: 左部分木 -> 根ノード -> 右部分木
inOrder(node.Left)
nums = append(nums, node.Val)
inOrder(node.Right)
}
/* 後順走査 */
func postOrder(node *TreeNode) {
if node == nil {
return
}
// 訪問優先順: 左部分木 -> 右部分木 -> 根ノード
postOrder(node.Left)
postOrder(node.Right)
nums = append(nums, node.Val)
}
@@ -0,0 +1,35 @@
// File: binary_tree_dfs_test.go
// Created Time: 2022-11-26
// Author: Reanon (793584285@qq.com)
package chapter_tree
import (
"fmt"
"testing"
. "github.com/krahets/hello-algo/pkg"
)
func TestPreInPostOrderTraversal(t *testing.T) {
/* 二分木を初期化 */
// ここでは、配列から直接二分木を生成する関数を利用する
root := SliceToTree([]any{1, 2, 3, 4, 5, 6, 7})
fmt.Println("\n二分木を初期化: ")
PrintTree(root)
// 先行順走査
nums = nil
preOrder(root)
fmt.Println("\n前順走査のノード出力シーケンス =", nums)
// 中順走査
nums = nil
inOrder(root)
fmt.Println("\n中順走査のノード出力シーケンス =", nums)
// 後順走査
nums = nil
postOrder(root)
fmt.Println("\n後順走査のノード出力シーケンス =", nums)
}
@@ -0,0 +1,41 @@
// File: binary_tree_test.go
// Created Time: 2022-11-25
// Author: Reanon (793584285@qq.com)
package chapter_tree
import (
"fmt"
"testing"
. "github.com/krahets/hello-algo/pkg"
)
func TestBinaryTree(t *testing.T) {
/* 二分木を初期化 */
// ノードを初期化
n1 := NewTreeNode(1)
n2 := NewTreeNode(2)
n3 := NewTreeNode(3)
n4 := NewTreeNode(4)
n5 := NewTreeNode(5)
// ノード間の参照(ポインタ)を構築する
n1.Left = n2
n1.Right = n3
n2.Left = n4
n2.Right = n5
fmt.Println("二分木を初期化")
PrintTree(n1)
/* ノードの挿入と削除 */
// ノードを挿入
p := NewTreeNode(0)
n1.Left = p
p.Left = n2
fmt.Println("ノード P を挿入後")
PrintTree(n1)
// ノードを削除
n1.Left = n2
fmt.Println("ノード P を削除後")
PrintTree(n1)
}