当前位置:首页 >娱乐 >使用 Go 语言实现二叉搜索树 使用实现索树比如红黑树

使用 Go 语言实现二叉搜索树 使用实现索树比如红黑树

2024-07-02 12:01:04 [百科] 来源:避面尹邢网

使用 Go 语言实现二叉搜索树

作者:yongxinz 开发 前端 最重要的使用实现索树就是它的有序性,在二叉搜索树中,叉搜每个节点的使用实现索树值都大于其左子树中的所有节点的值,并且小于其右子树中的叉搜所有节点的值。

二叉树是使用实现索树一种常见并且非常重要的数据结构,在很多项目中都能看到二叉树的叉搜身影。

它有很多变种,使用实现索树比如红黑树,叉搜常被用作 std::map 和 std::set 的使用实现索树底层实现;B 树和 B+ 树,广泛应用于数据库系统中。叉搜

使用 Go 语言实现二叉搜索树 使用实现索树比如红黑树

本文要介绍的使用实现索树二叉搜索树用的也很多,比如在开源项目 go-zero 中,叉搜就被用来做路由管理。使用实现索树

使用 Go 语言实现二叉搜索树 使用实现索树比如红黑树

这篇文章也算是叉搜一篇前导文章,介绍一些必备知识,使用实现索树下一篇再来介绍具体在 go-zero 中的应用。

使用 Go 语言实现二叉搜索树 使用实现索树比如红黑树

二叉搜索树的特点

最重要的就是它的有序性,在二叉搜索树中,每个节点的值都大于其左子树中的所有节点的值,并且小于其右子树中的所有节点的值。

图片图片

这意味着通过二叉搜索树可以快速实现对数据的查找和插入。

Go 语言实现

本文主要实现了以下几种方法:

  • Insert(t):插入一个节点
  • Search(t):判断节点是否在树中
  • InOrderTraverse():中序遍历
  • PreOrderTraverse():前序遍历
  • PostOrderTraverse():后序遍历
  • Min():返回最小值
  • Max():返回最大值
  • Remove(t):删除一个节点
  • String():打印一个树形结构

下面分别来介绍,首先定义一个节点:

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}

删除操作会复杂一些,分三种情况来考虑:

  1. 如果要删除的节点没有子节点,只需要直接将父节点中,指向要删除的节点指针置为 nil 即可
  2. 如果删除的节点只有一个子节点,只需要更新父节点中,指向要删除节点的指针,让它指向删除节点的子节点即可
  3. 如果删除的节点有两个子节点,我们需要找到这个节点右子树中的最小节点,把它替换到要删除的节点上。然后再删除这个最小节点,因为最小节点肯定没有左子节点,所以可以应用第二种情况删除这个最小节点即可

最后是一个打印树形结构的方法,在实际项目中其实并没有实际作用:

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,需要的同学可以自取。

源码地址:

  • https://github.com/yongxinz/go-example
责任编辑:武晓燕 来源: AlwaysBeta 二叉搜索测试

(责任编辑:焦点)

    推荐文章
    • 绿色债券迎密集发行期 银行参与绿色金融债券发行的热情高涨

      绿色债券迎密集发行期 银行参与绿色金融债券发行的热情高涨银行正在积极开展绿色金融业务,据北京商报记者11月10日不完全统计,今年以来,已有长沙银行、工商银行、南京银行、重庆银行、苏州银行、马鞍山农商行等多家银行获批或已获批发行绿色金融债券。除绿色债券外,在 ...[详细]
    • “新茶饮第二股”,会不会好过奈雪的茶?

      “新茶饮第二股”,会不会好过奈雪的茶?新茶饮的全盛时期已过。1月16日,不久前递交招股书的蜜雪冰城又有了新动作,这一次是做减法。多家媒体报道称,蜜雪冰城正在进行产品调整,包括“雪王爱喝水”“雪王霸汽”在内的瓶装饮料项目暂停代理。调整业务线 ...[详细]
    • 济南组建一支生物医药产业母基金,首期10亿元

      济南组建一支生物医药产业母基金,首期10亿元大力破解产业不聚焦、协同不密切问题, 济南市长清区谋划打造了生物医药、新能源汽车装备及零部件两大主导产业和网络视听、文旅康养两大特色产业。为此,长清区组建首期10亿元生物医药产业母基金,签约大华凯特、 ...[详细]
    • 八组数据回顾2023年我国信息技术发展

    • 险企“补血”渠道有望拓宽 永续债可补充核心二级资本

      险企“补血”渠道有望拓宽 永续债可补充核心二级资本继商业银行永续债后,保险版永续债也要来了。11月21日,北京商报记者获悉,近日,央行联合银保监会起草了《关于保险公司发行无固定期限资本债券有关事项的通知(征求意见稿)》(以下简称《意见稿》),并向社会 ...[详细]
    • 泡泡玛特供应链拓展至越南

      泡泡玛特供应链拓展至越南红星资本局1月18日消息,泡泡玛特在越南已经完成其海外工厂的第一批产品生产,标志着该公司全球供应体系的进一步扩展。随着海外市场的高速增长,泡泡玛特开始逐步扩大其全球供应体系。红星资本局了解到,泡泡玛特 ...[详细]
    • 启明星

      启明星编者按:日前,启明创投投资企业洛轲智能旗下极石汽车的中大型智能豪华SUV极石01实现首次OTA升级,此次升级致力于为用户带来更好的用车体验,包括精准识别车位,上线牵引模式及手机App多媒体遥控器,升级 ...[详细]
    • 清科创业2023Venture50:本源量子、玻色量子、图灵量子入选

      清科创业2023Venture50:本源量子、玻色量子、图灵量子入选C114讯 1月18日消息南山)近日,清科创业、投资界清科创业旗下资讯平台)发起的2023Venture50评选结果揭晓。此次评选延续风云榜与新芽榜TOP50双榜设置以及五大行业榜单评选。其中风云榜评 ...[详细]
    • 安逸花还清后还收费吗 取消方法是怎样的?

      安逸花还清后还收费吗 取消方法是怎样的?大家应该都知道,贷款都是要成本的,贷款平台会在借款成功后收取一定的费用,在安逸花借钱也一样。有不少人在安逸花上借的钱还清了想知道还会不会再收费,那么安逸花还清后还收费吗?这个要看具体是什么费用了,一起 ...[详细]
    • 格陵兰冰盖40年冰损失超5000平方公里

      格陵兰冰盖40年冰损失超5000平方公里美国科学家通过卫星观测研究显示,全球第二大冰盖格陵兰冰盖自1985年至今已损失约5091平方千米的冰。虽然这个量对海平面上升的贡献相对较小,但冰损失可能会影响大洋环流和全球热能分布。相关研究1月18日 ...[详细]
    热点阅读