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,57 @@
// File: n_queens.go
// Created Time: 2023-05-09
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
/* バックトラッキング:N クイーン */
func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, diags2 *[]bool) {
// すべての行への配置が完了したら、解を記録する
if row == n {
newState := make([][]string, len(*state))
for i, _ := range newState {
newState[i] = make([]string, len((*state)[0]))
copy(newState[i], (*state)[i])
}
*res = append(*res, newState)
return
}
// すべての列を走査
for col := 0; col < n; col++ {
// このマスに対応する主対角線と副対角線を計算
diag1 := row - col + n - 1
diag2 := row + col
// 枝刈り:そのマスの列、主対角線、副対角線にクイーンがあってはならない
if !(*cols)[col] && !(*diags1)[diag1] && !(*diags2)[diag2] {
// 試行:そのマスにクイーンを置く
(*state)[row][col] = "Q"
(*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = true, true, true
// 次の行に配置する
backtrack(row+1, n, state, res, cols, diags1, diags2)
// 戻す:そのマスを空きマスに戻す
(*state)[row][col] = "#"
(*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = false, false, false
}
}
}
/* N クイーンを解く */
func nQueens(n int) [][][]string {
// n*n の盤面を初期化する。'Q' はクイーン、'#' は空きマスを表す
state := make([][]string, n)
for i := 0; i < n; i++ {
row := make([]string, n)
for i := 0; i < n; i++ {
row[i] = "#"
}
state[i] = row
}
// 列にクイーンがあるか記録
cols := make([]bool, n)
diags1 := make([]bool, 2*n-1)
diags2 := make([]bool, 2*n-1)
res := make([][][]string, 0)
backtrack(0, n, &state, &res, &cols, &diags1, &diags2)
return res
}
@@ -0,0 +1,24 @@
// File: n_queens_test.go
// Created Time: 2023-05-14
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import (
"fmt"
"testing"
)
func TestNQueens(t *testing.T) {
n := 4
res := nQueens(n)
fmt.Println("入力された盤面の縦横は ", n)
fmt.Println("クイーンの配置パターンは ", len(res), " 通り")
for _, state := range res {
fmt.Println("--------------------")
for _, row := range state {
fmt.Println(row)
}
}
}
@@ -0,0 +1,33 @@
// File: permutation_test.go
// Created Time: 2023-05-09
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import (
"fmt"
"testing"
. "github.com/krahets/hello-algo/pkg"
)
func TestPermutationI(t *testing.T) {
/* 全順列 I */
nums := []int{1, 2, 3}
fmt.Printf("入力配列 nums = ")
PrintSlice(nums)
res := permutationsI(nums)
fmt.Printf("すべての順列 res = ")
fmt.Println(res)
}
func TestPermutationII(t *testing.T) {
nums := []int{1, 2, 2}
fmt.Printf("入力配列 nums = ")
PrintSlice(nums)
res := permutationsII(nums)
fmt.Printf("すべての順列 res = ")
fmt.Println(res)
}
@@ -0,0 +1,38 @@
// File: permutations_i.go
// Created Time: 2023-05-14
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
/* バックトラッキング:順列 I */
func backtrackI(state *[]int, choices *[]int, selected *[]bool, res *[][]int) {
// 状態の長さが要素数に等しければ、解を記録
if len(*state) == len(*choices) {
newState := append([]int{}, *state...)
*res = append(*res, newState)
}
// すべての選択肢を走査
for i := 0; i < len(*choices); i++ {
choice := (*choices)[i]
// 枝刈り:要素の重複選択を許可しない
if !(*selected)[i] {
// 試行: 選択を行い、状態を更新
(*selected)[i] = true
*state = append(*state, choice)
// 次の選択へ進む
backtrackI(state, choices, selected, res)
// バックトラック:選択を取り消し、前の状態に戻す
(*selected)[i] = false
*state = (*state)[:len(*state)-1]
}
}
}
/* 全順列 I */
func permutationsI(nums []int) [][]int {
res := make([][]int, 0)
state := make([]int, 0)
selected := make([]bool, len(nums))
backtrackI(&state, &nums, &selected, &res)
return res
}
@@ -0,0 +1,41 @@
// File: permutations_ii.go
// Created Time: 2023-05-14
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
/* バックトラッキング:順列 II */
func backtrackII(state *[]int, choices *[]int, selected *[]bool, res *[][]int) {
// 状態の長さが要素数に等しければ、解を記録
if len(*state) == len(*choices) {
newState := append([]int{}, *state...)
*res = append(*res, newState)
}
// すべての選択肢を走査
duplicated := make(map[int]struct{}, 0)
for i := 0; i < len(*choices); i++ {
choice := (*choices)[i]
// 枝刈り:要素の重複選択を許可せず、同値要素の重複選択も許可しない
if _, ok := duplicated[choice]; !ok && !(*selected)[i] {
// 試す: 選択を行って状態を更新
// 選択済みの要素値を記録
duplicated[choice] = struct{}{}
(*selected)[i] = true
*state = append(*state, choice)
// 次の選択へ進む
backtrackII(state, choices, selected, res)
// バックトラック:選択を取り消し、前の状態に戻す
(*selected)[i] = false
*state = (*state)[:len(*state)-1]
}
}
}
/* 全順列 II */
func permutationsII(nums []int) [][]int {
res := make([][]int, 0)
state := make([]int, 0)
selected := make([]bool, len(nums))
backtrackII(&state, &nums, &selected, &res)
return res
}
@@ -0,0 +1,22 @@
// File: preorder_traversal_i_compact.go
// Created Time: 2023-05-09
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import (
. "github.com/krahets/hello-algo/pkg"
)
/* 前順走査:例題 1 */
func preOrderI(root *TreeNode, res *[]*TreeNode) {
if root == nil {
return
}
if (root.Val).(int) == 7 {
// 解を記録
*res = append(*res, root)
}
preOrderI(root.Left, res)
preOrderI(root.Right, res)
}
@@ -0,0 +1,26 @@
// File: preorder_traversal_ii_compact.go
// Created Time: 2023-05-09
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import (
. "github.com/krahets/hello-algo/pkg"
)
/* 前順走査:例題 2 */
func preOrderII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) {
if root == nil {
return
}
// 試す
*path = append(*path, root)
if root.Val.(int) == 7 {
// 解を記録
*res = append(*res, append([]*TreeNode{}, *path...))
}
preOrderII(root.Left, res, path)
preOrderII(root.Right, res, path)
// バックトラック
*path = (*path)[:len(*path)-1]
}
@@ -0,0 +1,27 @@
// File: preorder_traversal_iii_compact.go
// Created Time: 2023-05-09
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import (
. "github.com/krahets/hello-algo/pkg"
)
/* 前順走査:例題 3 */
func preOrderIII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) {
// 枝刈り
if root == nil || root.Val == 3 {
return
}
// 試す
*path = append(*path, root)
if root.Val.(int) == 7 {
// 解を記録
*res = append(*res, append([]*TreeNode{}, *path...))
}
preOrderIII(root.Left, res, path)
preOrderIII(root.Right, res, path)
// バックトラック
*path = (*path)[:len(*path)-1]
}
@@ -0,0 +1,57 @@
// File: preorder_traversal_iii_template.go
// Created Time: 2023-05-09
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import (
. "github.com/krahets/hello-algo/pkg"
)
/* 現在の状態が解かどうかを判定 */
func isSolution(state *[]*TreeNode) bool {
return len(*state) != 0 && (*state)[len(*state)-1].Val == 7
}
/* 解を記録 */
func recordSolution(state *[]*TreeNode, res *[][]*TreeNode) {
*res = append(*res, append([]*TreeNode{}, *state...))
}
/* 現在の状態で、この選択が有効かどうかを判定 */
func isValid(state *[]*TreeNode, choice *TreeNode) bool {
return choice != nil && choice.Val != 3
}
/* 状態を更新 */
func makeChoice(state *[]*TreeNode, choice *TreeNode) {
*state = append(*state, choice)
}
/* 状態を元に戻す */
func undoChoice(state *[]*TreeNode, choice *TreeNode) {
*state = (*state)[:len(*state)-1]
}
/* バックトラッキング:例題 3 */
func backtrackIII(state *[]*TreeNode, choices *[]*TreeNode, res *[][]*TreeNode) {
// 解かどうかを確認
if isSolution(state) {
// 解を記録
recordSolution(state, res)
}
// すべての選択肢を走査
for _, choice := range *choices {
// 枝刈り:選択が妥当かを確認する
if isValid(state, choice) {
// 試行: 選択を行い、状態を更新
makeChoice(state, choice)
// 次の選択へ進む
temp := make([]*TreeNode, 0)
temp = append(temp, choice.Left, choice.Right)
backtrackIII(state, &temp, res)
// バックトラック:選択を取り消し、前の状態に戻す
undoChoice(state, choice)
}
}
}
@@ -0,0 +1,91 @@
// File: preorder_traversal_i_compact_test.go
// Created Time: 2023-05-09
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import (
"fmt"
"testing"
. "github.com/krahets/hello-algo/pkg"
)
func TestPreorderTraversalICompact(t *testing.T) {
/* 二分木を初期化 */
root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7})
fmt.Println("\n二分木を初期化")
PrintTree(root)
// 先行順走査
res := make([]*TreeNode, 0)
preOrderI(root, &res)
fmt.Println("\n値が 7 のノードをすべて出力")
for _, node := range res {
fmt.Printf("%v ", node.Val)
}
fmt.Println()
}
func TestPreorderTraversalIICompact(t *testing.T) {
/* 二分木を初期化 */
root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7})
fmt.Println("\n二分木を初期化")
PrintTree(root)
// 先行順走査
path := make([]*TreeNode, 0)
res := make([][]*TreeNode, 0)
preOrderII(root, &res, &path)
fmt.Println("\n根ノードからノード 7 までの経路をすべて出力")
for _, path := range res {
for _, node := range path {
fmt.Printf("%v ", node.Val)
}
fmt.Println()
}
}
func TestPreorderTraversalIIICompact(t *testing.T) {
/* 二分木を初期化 */
root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7})
fmt.Println("\n二分木を初期化")
PrintTree(root)
// 先行順走査
path := make([]*TreeNode, 0)
res := make([][]*TreeNode, 0)
preOrderIII(root, &res, &path)
fmt.Println("\n根ノードからノード 7 までの経路をすべて出力(経路に値が 3 のノードを含まない)")
for _, path := range res {
for _, node := range path {
fmt.Printf("%v ", node.Val)
}
fmt.Println()
}
}
func TestPreorderTraversalIIITemplate(t *testing.T) {
/* 二分木を初期化 */
root := SliceToTree([]any{1, 7, 3, 4, 5, 6, 7})
fmt.Println("\n二分木を初期化")
PrintTree(root)
// バックトラッキング法
res := make([][]*TreeNode, 0)
state := make([]*TreeNode, 0)
choices := make([]*TreeNode, 0)
choices = append(choices, root)
backtrackIII(&state, &choices, &res)
fmt.Println("\n根ノードからノード 7 までの経路をすべて出力(経路に値が 3 のノードを含まない)")
for _, path := range res {
for _, node := range path {
fmt.Printf("%v ", node.Val)
}
fmt.Println()
}
}
@@ -0,0 +1,42 @@
// File: subset_sum_i.go
// Created Time: 2023-06-24
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import "sort"
/* バックトラッキング:部分和 I */
func backtrackSubsetSumI(start, target int, state, choices *[]int, res *[][]int) {
// 部分集合の和が target に等しければ、解を記録
if target == 0 {
newState := append([]int{}, *state...)
*res = append(*res, newState)
return
}
// すべての選択肢を走査
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
for i := start; i < len(*choices); i++ {
// 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
// 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
if target-(*choices)[i] < 0 {
break
}
// 試す:選択を行い、target と start を更新
*state = append(*state, (*choices)[i])
// 次の選択へ進む
backtrackSubsetSumI(i, target-(*choices)[i], state, choices, res)
// バックトラック:選択を取り消し、前の状態に戻す
*state = (*state)[:len(*state)-1]
}
}
/* 部分和 I を解く */
func subsetSumI(nums []int, target int) [][]int {
state := make([]int, 0) // 状態(部分集合)
sort.Ints(nums) // nums をソート
start := 0 // 開始点を走査
res := make([][]int, 0) // 結果リスト(部分集合のリスト)
backtrackSubsetSumI(start, target, &state, &nums, &res)
return res
}
@@ -0,0 +1,37 @@
// File: subset_sum_i_naive.go
// Created Time: 2023-06-24
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
/* バックトラッキング:部分和 I */
func backtrackSubsetSumINaive(total, target int, state, choices *[]int, res *[][]int) {
// 部分集合の和が target に等しければ、解を記録
if target == total {
newState := append([]int{}, *state...)
*res = append(*res, newState)
return
}
// すべての選択肢を走査
for i := 0; i < len(*choices); i++ {
// 枝刈り:部分和が target を超える場合はその選択をスキップする
if total+(*choices)[i] > target {
continue
}
// 試行:選択を行い、要素と total を更新する
*state = append(*state, (*choices)[i])
// 次の選択へ進む
backtrackSubsetSumINaive(total+(*choices)[i], target, state, choices, res)
// バックトラック:選択を取り消し、前の状態に戻す
*state = (*state)[:len(*state)-1]
}
}
/* 部分和 I を解く(重複部分集合を含む) */
func subsetSumINaive(nums []int, target int) [][]int {
state := make([]int, 0) // 状態(部分集合)
total := 0 // 部分和
res := make([][]int, 0) // 結果リスト(部分集合のリスト)
backtrackSubsetSumINaive(total, target, &state, &nums, &res)
return res
}
@@ -0,0 +1,47 @@
// File: subset_sum_ii.go
// Created Time: 2023-06-24
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import "sort"
/* バックトラッキング:部分和 II */
func backtrackSubsetSumII(start, target int, state, choices *[]int, res *[][]int) {
// 部分集合の和が target に等しければ、解を記録
if target == 0 {
newState := append([]int{}, *state...)
*res = append(*res, newState)
return
}
// すべての選択肢を走査
// 枝刈り 2: start から走査し、重複する部分集合の生成を避ける
// 枝刈り 3: start から走査し、同じ要素の重複選択を避ける
for i := start; i < len(*choices); i++ {
// 枝刈り1:部分集合の和が target を超えたら、直ちにループを終了する
// 配列はソート済みで後続要素のほうが大きく、部分集合の和は必ず target を超えるため
if target-(*choices)[i] < 0 {
break
}
// 枝刈り4:この要素が左隣の要素と等しければ、その探索分岐は重複しているためスキップする
if i > start && (*choices)[i] == (*choices)[i-1] {
continue
}
// 試す:選択を行い、target と start を更新
*state = append(*state, (*choices)[i])
// 次の選択へ進む
backtrackSubsetSumII(i+1, target-(*choices)[i], state, choices, res)
// バックトラック:選択を取り消し、前の状態に戻す
*state = (*state)[:len(*state)-1]
}
}
/* 部分和 II を解く */
func subsetSumII(nums []int, target int) [][]int {
state := make([]int, 0) // 状態(部分集合)
sort.Ints(nums) // nums をソート
start := 0 // 開始点を走査
res := make([][]int, 0) // 結果リスト(部分集合のリスト)
backtrackSubsetSumII(start, target, &state, &nums, &res)
return res
}
@@ -0,0 +1,56 @@
// File: subset_sum_test.go
// Created Time: 2023-06-24
// Author: Reanon (793584285@qq.com)
package chapter_backtracking
import (
"fmt"
"strconv"
"testing"
. "github.com/krahets/hello-algo/pkg"
)
func TestSubsetSumINaive(t *testing.T) {
nums := []int{3, 4, 5}
target := 9
res := subsetSumINaive(nums, target)
fmt.Printf("target = " + strconv.Itoa(target) + ", 入力配列 nums = ")
PrintSlice(nums)
fmt.Println("合計が " + strconv.Itoa(target) + " に等しい部分集合 res = ")
for i := range res {
PrintSlice(res[i])
}
fmt.Println("この方法の出力結果には重複した集合が含まれることに注意してください")
}
func TestSubsetSumI(t *testing.T) {
nums := []int{3, 4, 5}
target := 9
res := subsetSumI(nums, target)
fmt.Printf("target = " + strconv.Itoa(target) + ", 入力配列 nums = ")
PrintSlice(nums)
fmt.Println("合計が " + strconv.Itoa(target) + " に等しい部分集合 res = ")
for i := range res {
PrintSlice(res[i])
}
}
func TestSubsetSumII(t *testing.T) {
nums := []int{4, 4, 5}
target := 9
res := subsetSumII(nums, target)
fmt.Printf("target = " + strconv.Itoa(target) + ", 入力配列 nums = ")
PrintSlice(nums)
fmt.Println("合計が " + strconv.Itoa(target) + " に等しい部分集合 res = ")
for i := range res {
PrintSlice(res[i])
}
}