diff --git a/codes/go/chapter_backtracking/n_queens.go b/codes/go/chapter_backtracking/n_queens.go new file mode 100644 index 000000000..23cc7e94f --- /dev/null +++ b/codes/go/chapter_backtracking/n_queens.go @@ -0,0 +1,55 @@ +// 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) + } + // 遍历所有列 + 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 + } + } +} + +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 +} diff --git a/codes/go/chapter_backtracking/n_queens_test.go b/codes/go/chapter_backtracking/n_queens_test.go new file mode 100644 index 000000000..5279a8280 --- /dev/null +++ b/codes/go/chapter_backtracking/n_queens_test.go @@ -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) + } + } +} diff --git a/codes/go/chapter_backtracking/permutation_test.go b/codes/go/chapter_backtracking/permutation_test.go new file mode 100644 index 000000000..cb0a1f22b --- /dev/null +++ b/codes/go/chapter_backtracking/permutation_test.go @@ -0,0 +1,33 @@ +// 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 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) +} diff --git a/codes/go/chapter_backtracking/permutations_i.go b/codes/go/chapter_backtracking/permutations_i.go new file mode 100644 index 000000000..ceebbf4ea --- /dev/null +++ b/codes/go/chapter_backtracking/permutations_i.go @@ -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 +} diff --git a/codes/go/chapter_backtracking/permutations_ii.go b/codes/go/chapter_backtracking/permutations_ii.go new file mode 100644 index 000000000..3a137083a --- /dev/null +++ b/codes/go/chapter_backtracking/permutations_ii.go @@ -0,0 +1,45 @@ +// File: permutations_i.go +// Created Time: 2023-05-14 +// Author: Reanon (793584285@qq.com) + +// File: permutations_i.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) + // 进行下一轮选择 + backtrackI(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 +} diff --git a/codes/go/chapter_backtracking/preorder_traversal_i_compact.go b/codes/go/chapter_backtracking/preorder_traversal_i_compact.go new file mode 100644 index 000000000..2b9226579 --- /dev/null +++ b/codes/go/chapter_backtracking/preorder_traversal_i_compact.go @@ -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" +) + +/* 前序遍历:例题一 */ +func preOrderI(root *TreeNode, res *[]*TreeNode) { + if root == nil { + return + } + if int(root.Val) == 7 { + // 记录解 + *res = append(*res, root) + } + preOrderI(root.Left, res) + preOrderI(root.Right, res) +} diff --git a/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go b/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go new file mode 100644 index 000000000..553649388 --- /dev/null +++ b/codes/go/chapter_backtracking/preorder_traversal_ii_compact.go @@ -0,0 +1,26 @@ +// 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" +) + +/* 前序遍历:例题二 */ +func preOrderII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) { + if root == nil { + return + } + // 尝试 + *path = append(*path, root) + if int(root.Val) == 7 { + // 记录解 + *res = append(*res, *path) + } + preOrderII(root.Left, res, path) + preOrderII(root.Right, res, path) + // 回退 + *path = (*path)[:len(*path)-1] +} diff --git a/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go b/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go new file mode 100644 index 000000000..fcf1b86b2 --- /dev/null +++ b/codes/go/chapter_backtracking/preorder_traversal_iii_compact.go @@ -0,0 +1,27 @@ +// 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" +) + +/* 前序遍历:例题三 */ +func preOrderIII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) { + // 剪枝 + if root == nil || root.Val == 3 { + return + } + // 尝试 + *path = append(*path, root) + if int(root.Val) == 7 { + // 记录解 + *res = append(*res, *path) + } + preOrderIII(root.Left, res, path) + preOrderIII(root.Right, res, path) + // 回退 + *path = (*path)[:len(*path)-1] +} diff --git a/codes/go/chapter_backtracking/preorder_traversal_iii_template.go b/codes/go/chapter_backtracking/preorder_traversal_iii_template.go new file mode 100644 index 000000000..9e299860a --- /dev/null +++ b/codes/go/chapter_backtracking/preorder_traversal_iii_template.go @@ -0,0 +1,58 @@ +// 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" +) + +/* 判断当前状态是否为解 */ +func isSolution(state *[]*TreeNode) bool { + return len(*state) != 0 && (*state)[len(*state)-1].Val == 7 +} + +/* 记录解 */ +func recordSolution(state *[]*TreeNode, res *[][]*TreeNode) { + *res = append(*res, *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] +} + +/* 回溯算法:例题三 */ +func backtrackIII(state *[]*TreeNode, choices *[]*TreeNode, res *[][]*TreeNode) { + // 检查是否为解 + if isSolution(state) { + // 记录解 + recordSolution(state, res) + return + } + // 遍历所有选择 + 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) + } + } +} diff --git a/codes/go/chapter_backtracking/preorder_traversal_test.go b/codes/go/chapter_backtracking/preorder_traversal_test.go new file mode 100644 index 000000000..b71d429b8 --- /dev/null +++ b/codes/go/chapter_backtracking/preorder_traversal_test.go @@ -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 := ArrToTree([]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 := ArrToTree([]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 := ArrToTree([]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 := ArrToTree([]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() + } +}