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..955ebdbf2 --- /dev/null +++ b/codes/go/chapter_tree/binary_search_tree_test.go @@ -0,0 +1,41 @@ +// 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{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + 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(2) + t.Log("删除结点 2 后的二叉树为: ") + bst.Print() + bst.Remove(4) + t.Log("删除结点 4 后的二叉树为: ") + bst.Print() +} 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) +} 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) +} 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/codes/go/pkg/tree_node.go b/codes/go/pkg/tree_node.go index 571dfac24..b62dc6056 100644 --- a/codes/go/pkg/tree_node.go +++ b/codes/go/pkg/tree_node.go @@ -51,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, 16) + arr := make([]any, 0) queue := list.New() queue.PushBack(root) for queue.Len() > 0 { @@ -65,7 +65,8 @@ func TreeToArray(root *TreeNode) []int { queue.PushBack(node.Left) queue.PushBack(node.Right) } else { - arr = append(arr, -1) + // node don't exist. + arr = append(arr, nil) } } return arr @@ -73,19 +74,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 +102,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)) } 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) 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 以下的树」。 ## 二叉树常见术语