From fae6c6ac5b8b491abead0d642bbbd6a98fa18487 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Sat, 26 Nov 2022 11:11:40 +0800 Subject: [PATCH 1/6] fix(pkg): amend tree node --- codes/go/pkg/tree_node.go | 17 ++++++++++------- codes/go/pkg/tree_node_test.go | 3 +++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/codes/go/pkg/tree_node.go b/codes/go/pkg/tree_node.go index 571dfac24..6175688e4 100644 --- a/codes/go/pkg/tree_node.go +++ b/codes/go/pkg/tree_node.go @@ -9,6 +9,9 @@ import ( "fmt" ) +// nodeNotExist represents node don't exist. +const nodeNotExist int = -1 + type TreeNode struct { Val int Left *TreeNode @@ -55,7 +58,7 @@ func TreeToArray(root *TreeNode) []int { if root == nil { return []int{} } - arr := make([]int, 16) + arr := make([]int, 0) queue := list.New() queue.PushBack(root) for queue.Len() > 0 { @@ -65,7 +68,7 @@ func TreeToArray(root *TreeNode) []int { queue.PushBack(node.Left) queue.PushBack(node.Right) } else { - arr = append(arr, -1) + arr = append(arr, nodeNotExist) } } return arr @@ -73,19 +76,19 @@ func TreeToArray(root *TreeNode) []int { // PrintTree Print a binary tree func PrintTree(root *TreeNode) { - PrintTreeHelper(root, nil, false) + printTreeHelper(root, nil, false) } -// PrintTreeHelper Help to print a binary tree, hide more details +// printTreeHelper Help to print a binary tree, hide more details // This tree printer is borrowed from TECHIE DELIGHT // https://www.techiedelight.com/c-program-print-binary-tree/ -func PrintTreeHelper(root *TreeNode, prev *trunk, isLeft bool) { +func printTreeHelper(root *TreeNode, prev *trunk, isLeft bool) { if root == nil { return } prevStr := " " trunk := newTrunk(prev, prevStr) - PrintTreeHelper(root.Right, trunk, true) + printTreeHelper(root.Right, trunk, true) if prev == nil { trunk.str = "———" } else if isLeft { @@ -101,7 +104,7 @@ func PrintTreeHelper(root *TreeNode, prev *trunk, isLeft bool) { prev.str = prevStr } trunk.str = " |" - PrintTreeHelper(root.Left, trunk, false) + printTreeHelper(root.Left, trunk, false) } // trunk Help to Print tree structure diff --git a/codes/go/pkg/tree_node_test.go b/codes/go/pkg/tree_node_test.go index c761ecb83..ffbdf6ff0 100644 --- a/codes/go/pkg/tree_node_test.go +++ b/codes/go/pkg/tree_node_test.go @@ -12,4 +12,7 @@ func TestTreeNode(t *testing.T) { // print tree PrintTree(node) + + // tree to arr + t.Log(TreeToArray(node)) } From db5ca936b615c365793f893c5c810de9ef27bbb9 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Sat, 26 Nov 2022 13:24:35 +0800 Subject: [PATCH 2/6] feat(tree): add binary tree in golang --- codes/go/chapter_tree/binary_tree_test.go | 2 +- docs/chapter_tree/binary_tree.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codes/go/chapter_tree/binary_tree_test.go b/codes/go/chapter_tree/binary_tree_test.go index ee77edc31..2c05076db 100644 --- a/codes/go/chapter_tree/binary_tree_test.go +++ b/codes/go/chapter_tree/binary_tree_test.go @@ -11,7 +11,7 @@ import ( func TestBinaryTree(t *testing.T) { /* 初始化二叉树 */ - // 初始化节点 + // 初始化结点 n1 := NewTreeNode(1) n2 := NewTreeNode(2) n3 := NewTreeNode(3) diff --git a/docs/chapter_tree/binary_tree.md b/docs/chapter_tree/binary_tree.md index 07a9a648e..a53507da6 100644 --- a/docs/chapter_tree/binary_tree.md +++ b/docs/chapter_tree/binary_tree.md @@ -24,7 +24,7 @@ comments: true

Fig. 子结点与子树

-需要注意,父结点、子结点、子树是可以向下递推的。例如,如果将上图的「结点 2」看作父结点,那么其左子节点和右子结点分别为「结点 4」和「结点 5」,左子树和右子树分别为「结点 4 以下的树」和「结点 5 以下的树」。 +需要注意,父结点、子结点、子树是可以向下递推的。例如,如果将上图的「结点 2」看作父结点,那么其左子结点和右子结点分别为「结点 4」和「结点 5」,左子树和右子树分别为「结点 4 以下的树」和「结点 5 以下的树」。 ## 二叉树常见术语 From 2e72ce8eeb70f5c32285a5c7f0ac114cd836898a Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Sat, 26 Nov 2022 13:25:13 +0800 Subject: [PATCH 3/6] feat(tree): add binary search tree in golang --- codes/go/chapter_tree/binary_search_tree.go | 165 +++++++++++++++++- .../chapter_tree/binary_search_tree_test.go | 44 +++++ docs/chapter_tree/binary_search_tree.md | 4 +- 3 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 codes/go/chapter_tree/binary_search_tree_test.go diff --git a/codes/go/chapter_tree/binary_search_tree.go b/codes/go/chapter_tree/binary_search_tree.go index c21dc1444..26eb94d73 100644 --- a/codes/go/chapter_tree/binary_search_tree.go +++ b/codes/go/chapter_tree/binary_search_tree.go @@ -1,5 +1,5 @@ // File: binary_search_tree.go -// Created Time: 2022-11-25 +// Created Time: 2022-11-26 // Author: Reanon (793584285@qq.com) package chapter_tree @@ -14,7 +14,166 @@ type BinarySearchTree struct { } func NewBinarySearchTree(nums []int) *BinarySearchTree { - // 排序数组 + // sorting array sort.Ints(nums) - return nil + root := buildBinarySearchTree(nums, 0, len(nums)-1) + return &BinarySearchTree{ + root: root, + } +} + +// GetRoot Get the root node of binary search tree +func (bst *BinarySearchTree) GetRoot() *TreeNode { + return bst.root +} + +// GetMin Get node with the min value +func (bst *BinarySearchTree) GetMin(node *TreeNode) *TreeNode { + if node == nil { + return node + } + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + for node.Left != nil { + node = node.Left + } + return node +} + +// GetInorderNext Get node inorder next +func (bst *BinarySearchTree) GetInorderNext(node *TreeNode) *TreeNode { + if node == nil || node.Right == nil { + return node + } + node = node.Right + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + for node.Left != nil { + node = node.Left + } + return node +} + +// Search node of binary search tree +func (bst *BinarySearchTree) Search(num int) *TreeNode { + node := bst.root + // 循环查找,越过叶结点后跳出 + for node != nil { + if node.Val < num { + // 目标结点在 root 的右子树中 + node = node.Right + } else if node.Val > num { + // 目标结点在 root 的左子树中 + node = node.Left + } else { + // 找到目标结点,跳出循环 + break + } + } + // 返回目标结点 + return node +} + +// Insert node of binary search tree +func (bst *BinarySearchTree) Insert(num int) *TreeNode { + cur := bst.root + // 若树为空,直接提前返回 + if cur == nil { + return nil + } + // 待插入结点之前的结点位置 + var prev *TreeNode = nil + // 循环查找,越过叶结点后跳出 + for cur != nil { + if cur.Val == num { + return nil + } + prev = cur + if cur.Val < num { + cur = cur.Right + } else { + cur = cur.Left + } + } + // 插入结点 + node := NewTreeNode(num) + if prev.Val < num { + prev.Right = node + } else { + prev.Left = node + } + return cur +} + +// Remove node of binary search tree +func (bst *BinarySearchTree) Remove(num int) *TreeNode { + cur := bst.root + // 若树为空,直接提前返回 + if cur == nil { + return nil + } + // 待删除结点之前的结点位置 + var prev *TreeNode = nil + // 循环查找,越过叶结点后跳出 + for cur != nil { + if cur.Val == num { + break + } + prev = cur + // 待删除结点在右子树中 + if cur.Val < num { + cur = cur.Right + } else { + // 待删除结点在左子树中 + cur = cur.Left + } + } + // 若无待删除结点,则直接返回 + if cur == nil { + return nil + } + // 子结点数为 0 或 1 + if cur.Left == nil || cur.Right == nil { + var child *TreeNode = nil + // 取出待删除结点的子结点 + if cur.Left != nil { + child = cur.Left + } else { + child = cur.Right + } + // 将子结点替换为待删除结点 + if prev.Left == cur { + prev.Left = child + } else { + prev.Right = child + } + + } else { // 子结点数为 2 + // 获取中序遍历中待删除结点 cur 的下一个结点 + next := bst.GetInorderNext(cur) + temp := next.Val + // 递归删除结点 next + bst.Remove(next.Val) + // 将 next 的值复制给 cur + cur.Val = temp + } + // TODO: add error handler, don't return node + return cur +} + +// buildBinarySearchTree Build a binary search tree from array. +func buildBinarySearchTree(nums []int, left, right int) *TreeNode { + if left > right { + return nil + } + // 将数组中间结点作为根结点 + middle := left + (right-left)>>1 + root := NewTreeNode(nums[middle]) + // 递归构建左子树和右子树 + root.Left = buildBinarySearchTree(nums, left, middle-1) + root.Right = buildBinarySearchTree(nums, middle+1, right) + return root +} + +// Print binary search tree +func (bst *BinarySearchTree) Print() { + PrintTree(bst.root) } diff --git a/codes/go/chapter_tree/binary_search_tree_test.go b/codes/go/chapter_tree/binary_search_tree_test.go new file mode 100644 index 000000000..45d59bd8d --- /dev/null +++ b/codes/go/chapter_tree/binary_search_tree_test.go @@ -0,0 +1,44 @@ +// File: binary_search_tree_test.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import "testing" + +func TestBinarySearchTree(t *testing.T) { + nums := []int{8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7} + bst := NewBinarySearchTree(nums) + t.Log("初始化的二叉树为: ") + bst.Print() + + // 获取根结点 + node := bst.GetRoot() + t.Log("二叉树的根结点为: ", node.Val) + // 获取最小的结点 + node = bst.GetMin(bst.GetRoot()) + t.Log("二叉树的最小结点为: ", node.Val) + + // 查找结点 + node = bst.Search(5) + t.Log("查找到的结点对象为", node, ",结点值 = ", node.Val) + + // 插入结点 + node = bst.Insert(16) + t.Log("插入结点后 16 的二叉树为: ") + bst.Print() + + // 删除结点 + bst.Remove(1) + t.Log("删除结点 1 后的二叉树为: ") + bst.Print() + bst.Remove(12) + t.Log("删除结点 12 后的二叉树为: ") + bst.Print() + bst.Remove(2) + t.Log("删除结点 2 后的二叉树为: ") + bst.Print() + bst.Remove(4) + t.Log("删除结点 4 后的二叉树为: ") + bst.Print() +} diff --git a/docs/chapter_tree/binary_search_tree.md b/docs/chapter_tree/binary_search_tree.md index a59493b25..a9514ec79 100644 --- a/docs/chapter_tree/binary_search_tree.md +++ b/docs/chapter_tree/binary_search_tree.md @@ -63,11 +63,11 @@ comments: true 给定一个待插入元素 `num` ,为了保持二叉搜索树 “左子树 < 根结点 < 右子树” 的性质,插入操作分为两步: -1. **查找插入位置:** 与查找操作类似,我们从根结点出发,根据当前节点值和 `num` 的大小关系循环向下搜索,直到越过叶结点(遍历到 $\text{null}$ )时跳出循环; +1. **查找插入位置:** 与查找操作类似,我们从根结点出发,根据当前结点值和 `num` 的大小关系循环向下搜索,直到越过叶结点(遍历到 $\text{null}$ )时跳出循环; 2. **在该位置插入结点:** 初始化结点 `num` ,将该结点放到 $\text{null}$ 的位置 ; -二叉搜索树不允许存在重复节点,否则将会违背其定义。因此若待插入结点在树中已经存在,则不执行插入,直接返回即可。 +二叉搜索树不允许存在重复结点,否则将会违背其定义。因此若待插入结点在树中已经存在,则不执行插入,直接返回即可。 ![bst_insert](binary_search_tree.assets/bst_insert.png) From 0f89475e0e8348febfd0d5161d8ebf2e6ffe392a Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Sat, 26 Nov 2022 14:03:27 +0800 Subject: [PATCH 4/6] feat(tree): add binary tree bfs in golang --- codes/go/chapter_tree/binary_tree_bfs.go | 35 +++++++++++++++++++ codes/go/chapter_tree/binary_tree_bfs_test.go | 22 ++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 codes/go/chapter_tree/binary_tree_bfs.go create mode 100644 codes/go/chapter_tree/binary_tree_bfs_test.go diff --git a/codes/go/chapter_tree/binary_tree_bfs.go b/codes/go/chapter_tree/binary_tree_bfs.go new file mode 100644 index 000000000..d04102f99 --- /dev/null +++ b/codes/go/chapter_tree/binary_tree_bfs.go @@ -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" +) + +// levelOrder Breadth First Search +func levelOrder(root *TreeNode) []int { + // Let container.list as queue + queue := list.New() + // 初始化队列,加入根结点 + queue.PushBack(root) + // 初始化一个切片,用于保存遍历序列 + nums := make([]int, 0) + for queue.Len() > 0 { + // poll + 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 +} diff --git a/codes/go/chapter_tree/binary_tree_bfs_test.go b/codes/go/chapter_tree/binary_tree_bfs_test.go new file mode 100644 index 000000000..48d3d383b --- /dev/null +++ b/codes/go/chapter_tree/binary_tree_bfs_test.go @@ -0,0 +1,22 @@ +// File: binary_tree_bfs_test.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + . "github.com/krahets/hello-algo/pkg" + "testing" +) + +func TestLevelOrder(t *testing.T) { + /* 初始化二叉树 */ + // 这里借助了一个从数组直接生成二叉树的函数 + root := ArrayToTree([]int{1, 2, 3, 4, 5, 6, 7}) + t.Log("初始化二叉树: ") + PrintTree(root) + + // 层序遍历 + nums := levelOrder(root) + t.Log("层序遍历的结点打印序列 = ", nums) +} From e62d663630fec78c66aed2c92964f0a0d8adf855 Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Sat, 26 Nov 2022 14:36:43 +0800 Subject: [PATCH 5/6] feat(tree): add binary tree dfs in golang --- codes/go/chapter_tree/binary_tree_dfs.go | 63 +++++++++++++++++++ codes/go/chapter_tree/binary_tree_dfs_test.go | 30 +++++++++ 2 files changed, 93 insertions(+) create mode 100644 codes/go/chapter_tree/binary_tree_dfs.go create mode 100644 codes/go/chapter_tree/binary_tree_dfs_test.go diff --git a/codes/go/chapter_tree/binary_tree_dfs.go b/codes/go/chapter_tree/binary_tree_dfs.go new file mode 100644 index 000000000..c97aa17df --- /dev/null +++ b/codes/go/chapter_tree/binary_tree_dfs.go @@ -0,0 +1,63 @@ +// 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" +) + +// preOrder 前序遍历 +func preOrder(root *TreeNode) (nums []int) { + var preOrderHelper func(node *TreeNode) + // 匿名函数 + preOrderHelper = func(node *TreeNode) { + if node == nil { + return + } + // 访问优先级:根结点 -> 左子树 -> 右子树 + nums = append(nums, node.Val) + preOrderHelper(node.Left) + preOrderHelper(node.Right) + } + // 函数调用 + preOrderHelper(root) + return +} + +// inOrder 中序遍历 +func inOrder(root *TreeNode) (nums []int) { + var inOrderHelper func(node *TreeNode) + // 匿名函数 + inOrderHelper = func(node *TreeNode) { + if node == nil { + return + } + // 访问优先级:左子树 -> 根结点 -> 右子树 + inOrderHelper(node.Left) + nums = append(nums, node.Val) + inOrderHelper(node.Right) + } + // 函数调用 + inOrderHelper(root) + return +} + +// postOrder 后序遍历 +func postOrder(root *TreeNode) (nums []int) { + var postOrderHelper func(node *TreeNode) + // 匿名函数 + postOrderHelper = func(node *TreeNode) { + if node == nil { + return + } + // 访问优先级:左子树 -> 右子树 -> 根结点 + postOrderHelper(node.Left) + postOrderHelper(node.Right) + nums = append(nums, node.Val) + } + // 函数调用 + postOrderHelper(root) + return +} diff --git a/codes/go/chapter_tree/binary_tree_dfs_test.go b/codes/go/chapter_tree/binary_tree_dfs_test.go new file mode 100644 index 000000000..36ebb0bc4 --- /dev/null +++ b/codes/go/chapter_tree/binary_tree_dfs_test.go @@ -0,0 +1,30 @@ +// File: binary_tree_dfs_test.go +// Created Time: 2022-11-26 +// Author: Reanon (793584285@qq.com) + +package chapter_tree + +import ( + . "github.com/krahets/hello-algo/pkg" + "testing" +) + +func TestPreInPostOrderTraversal(t *testing.T) { + /* 初始化二叉树 */ + // 这里借助了一个从数组直接生成二叉树的函数 + root := ArrayToTree([]int{1, 2, 3, 4, 5, 6, 7}) + t.Log("初始化二叉树: ") + PrintTree(root) + + // 前序遍历 + nums := preOrder(root) + t.Log("前序遍历的结点打印序列 = ", nums) + + // 中序遍历 + nums = inOrder(root) + t.Log("中序遍历的结点打印序列 = ", nums) + + // 后序遍历 + nums = postOrder(root) + t.Log("后序遍历的结点打印序列 = ", nums) +} From 913cf38d0bb6c0a58b49b0603436bd689ed4e3fd Mon Sep 17 00:00:00 2001 From: reanon <793584285@qq.com> Date: Sat, 26 Nov 2022 16:32:34 +0800 Subject: [PATCH 6/6] fix(tree): make go code as consistent as possible with java code --- codes/go/chapter_tree/binary_search_tree_test.go | 5 +---- codes/go/pkg/tree_node.go | 12 +++++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/codes/go/chapter_tree/binary_search_tree_test.go b/codes/go/chapter_tree/binary_search_tree_test.go index 45d59bd8d..955ebdbf2 100644 --- a/codes/go/chapter_tree/binary_search_tree_test.go +++ b/codes/go/chapter_tree/binary_search_tree_test.go @@ -7,7 +7,7 @@ package chapter_tree import "testing" func TestBinarySearchTree(t *testing.T) { - nums := []int{8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7} + nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} bst := NewBinarySearchTree(nums) t.Log("初始化的二叉树为: ") bst.Print() @@ -32,9 +32,6 @@ func TestBinarySearchTree(t *testing.T) { bst.Remove(1) t.Log("删除结点 1 后的二叉树为: ") bst.Print() - bst.Remove(12) - t.Log("删除结点 12 后的二叉树为: ") - bst.Print() bst.Remove(2) t.Log("删除结点 2 后的二叉树为: ") bst.Print() diff --git a/codes/go/pkg/tree_node.go b/codes/go/pkg/tree_node.go index 6175688e4..b62dc6056 100644 --- a/codes/go/pkg/tree_node.go +++ b/codes/go/pkg/tree_node.go @@ -9,9 +9,6 @@ import ( "fmt" ) -// nodeNotExist represents node don't exist. -const nodeNotExist int = -1 - type TreeNode struct { Val int Left *TreeNode @@ -54,11 +51,11 @@ func ArrayToTree(arr []int) *TreeNode { } // TreeToArray Serialize a binary tree to a list -func TreeToArray(root *TreeNode) []int { +func TreeToArray(root *TreeNode) []any { if root == nil { - return []int{} + return []any{} } - arr := make([]int, 0) + arr := make([]any, 0) queue := list.New() queue.PushBack(root) for queue.Len() > 0 { @@ -68,7 +65,8 @@ func TreeToArray(root *TreeNode) []int { queue.PushBack(node.Left) queue.PushBack(node.Right) } else { - arr = append(arr, nodeNotExist) + // node don't exist. + arr = append(arr, nil) } } return arr