二叉树是使用实现索树一种常见并且非常重要的数据结构,在很多项目中都能看到二叉树的叉搜身影。
它有很多变种,使用实现索树比如红黑树,叉搜常被用作 std::map 和 std::set 的使用实现索树底层实现;B 树和 B+ 树,广泛应用于数据库系统中。叉搜
本文要介绍的使用实现索树二叉搜索树用的也很多,比如在开源项目 go-zero 中,叉搜就被用来做路由管理。使用实现索树
这篇文章也算是叉搜一篇前导文章,介绍一些必备知识,使用实现索树下一篇再来介绍具体在 go-zero 中的应用。
最重要的就是它的有序性,在二叉搜索树中,每个节点的值都大于其左子树中的所有节点的值,并且小于其右子树中的所有节点的值。
图片
这意味着通过二叉搜索树可以快速实现对数据的查找和插入。
本文主要实现了以下几种方法:
下面分别来介绍,首先定义一个节点:
type Node struct { key int value Item left *Node //left right *Node //right}
定义树的结构体,其中包含了锁,是线程安全的:
type ItemBinarySearchTree struct { root *Node lock sync.RWMutex}
插入操作:
func (bst *ItemBinarySearchTree) Insert(key int, value Item) { bst.lock.Lock() defer bst.lock.Unlock() n := &Node{ key, value, nil, nil} if bst.root == nil { bst.root = n } else { insertNode(bst.root, n) }}// internal function to find the correct place for a node in a treefunc insertNode(node, newNode *Node) { if newNode.key < node.key { if node.left == nil { node.left = newNode } else { insertNode(node.left, newNode) } } else { if node.right == nil { node.right = newNode } else { insertNode(node.right, newNode) } }}
在插入时,需要判断插入节点和当前节点的大小关系,保证搜索树的有序性。
中序遍历:
func (bst *ItemBinarySearchTree) InOrderTraverse(f func(Item)) { bst.lock.RLock() defer bst.lock.RUnlock() inOrderTraverse(bst.root, f)}// internal recursive function to traverse in orderfunc inOrderTraverse(n *Node, f func(Item)) { if n != nil { inOrderTraverse(n.left, f) f(n.value) inOrderTraverse(n.right, f) }}
前序遍历:
func (bst *ItemBinarySearchTree) PreOrderTraverse(f func(Item)) { bst.lock.Lock() defer bst.lock.Unlock() preOrderTraverse(bst.root, f)}// internal recursive function to traverse pre orderfunc preOrderTraverse(n *Node, f func(Item)) { if n != nil { f(n.value) preOrderTraverse(n.left, f) preOrderTraverse(n.right, f) }}
后序遍历:
func (bst *ItemBinarySearchTree) PostOrderTraverse(f func(Item)) { bst.lock.Lock() defer bst.lock.Unlock() postOrderTraverse(bst.root, f)}// internal recursive function to traverse post orderfunc postOrderTraverse(n *Node, f func(Item)) { if n != nil { postOrderTraverse(n.left, f) postOrderTraverse(n.right, f) f(n.value) }}
返回最小值:
func (bst *ItemBinarySearchTree) Min() *Item { bst.lock.RLock() defer bst.lock.RUnlock() n := bst.root if n == nil { return nil } for { if n.left == nil { return &n.value } n = n.left }}
由于树的有序性,想要得到最小值,一直向左查找就可以了。
返回最大值:
func (bst *ItemBinarySearchTree) Max() *Item { bst.lock.RLock() defer bst.lock.RUnlock() n := bst.root if n == nil { return nil } for { if n.right == nil { return &n.value } n = n.right }}
查找节点是否存在:
func (bst *ItemBinarySearchTree) Search(key int) bool { bst.lock.RLock() defer bst.lock.RUnlock() return search(bst.root, key)}// internal recursive function to search an item in the treefunc search(n *Node, key int) bool { if n == nil { return false } if key < n.key { return search(n.left, key) } if key > n.key { return search(n.right, key) } return true}
删除节点:
func (bst *ItemBinarySearchTree) Remove(key int) { bst.lock.Lock() defer bst.lock.Unlock() remove(bst.root, key)}// internal recursive function to remove an itemfunc remove(node *Node, key int) *Node { if node == nil { return nil } if key < node.key { node.left = remove(node.left, key) return node } if key > node.key { node.right = remove(node.right, key) return node } // key == node.key if node.left == nil && node.right == nil { node = nil return nil } if node.left == nil { node = node.right return node } if node.right == nil { node = node.left return node } leftmostrightside := node.right for { //find smallest value on the right side if leftmostrightside != nil && leftmostrightside.left != nil { leftmostrightside = leftmostrightside.left } else { break } } node.key, node.value = leftmostrightside.key, leftmostrightside.value node.right = remove(node.right, node.key) return node}
删除操作会复杂一些,分三种情况来考虑:
最后是一个打印树形结构的方法,在实际项目中其实并没有实际作用:
func (bst *ItemBinarySearchTree) String() { bst.lock.Lock() defer bst.lock.Unlock() fmt.Println("------------------------------------------------") stringify(bst.root, 0) fmt.Println("------------------------------------------------")}// internal recursive function to print a treefunc stringify(n *Node, level int) { if n != nil { format := "" for i := 0; i < level; i++ { format += " " } format += "---[ " level++ stringify(n.left, level) fmt.Printf(format+"%d\n", n.key) stringify(n.right, level) }}
下面是一段测试代码:
func fillTree(bst *ItemBinarySearchTree) { bst.Insert(8, "8") bst.Insert(4, "4") bst.Insert(10, "10") bst.Insert(2, "2") bst.Insert(6, "6") bst.Insert(1, "1") bst.Insert(3, "3") bst.Insert(5, "5") bst.Insert(7, "7") bst.Insert(9, "9")}func TestInsert(t *testing.T) { fillTree(&bst) bst.String() bst.Insert(11, "11") bst.String()}// isSameSlice returns true if the 2 slices are identicalfunc isSameSlice(a, b []string) bool { if a == nil && b == nil { return true } if a == nil || b == nil { return false } if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true}func TestInOrderTraverse(t *testing.T) { var result []string bst.InOrderTraverse(func(i Item) { result = append(result, fmt.Sprintf("%s", i)) }) if !isSameSlice(result, []string{ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"}) { t.Errorf("Traversal order incorrect, got %v", result) }}func TestPreOrderTraverse(t *testing.T) { var result []string bst.PreOrderTraverse(func(i Item) { result = append(result, fmt.Sprintf("%s", i)) }) if !isSameSlice(result, []string{ "8", "4", "2", "1", "3", "6", "5", "7", "10", "9", "11"}) { t.Errorf("Traversal order incorrect, got %v instead of %v", result, []string{ "8", "4", "2", "1", "3", "6", "5", "7", "10", "9", "11"}) }}func TestPostOrderTraverse(t *testing.T) { var result []string bst.PostOrderTraverse(func(i Item) { result = append(result, fmt.Sprintf("%s", i)) }) if !isSameSlice(result, []string{ "1", "3", "2", "5", "7", "6", "4", "9", "11", "10", "8"}) { t.Errorf("Traversal order incorrect, got %v instead of %v", result, []string{ "1", "3", "2", "5", "7", "6", "4", "9", "11", "10", "8"}) }}func TestMin(t *testing.T) { if fmt.Sprintf("%s", *bst.Min()) != "1" { t.Errorf("min should be 1") }}func TestMax(t *testing.T) { if fmt.Sprintf("%s", *bst.Max()) != "11" { t.Errorf("max should be 11") }}func TestSearch(t *testing.T) { if !bst.Search(1) || !bst.Search(8) || !bst.Search(11) { t.Errorf("search not working") }}func TestRemove(t *testing.T) { bst.Remove(1) if fmt.Sprintf("%s", *bst.Min()) != "2" { t.Errorf("min should be 2") }}
上文中的全部源码都是经过测试的,可以直接运行,并且已经上传到了 GitHub,需要的同学可以自取。
源码地址:
(责任编辑:焦点)
清科创业2023Venture50:本源量子、玻色量子、图灵量子入选