Go的数据结构与实现【Binary Search Tree】

介绍

本文用Go将实现二叉搜索树数据结构,以及常见的一些方法

二叉树

二叉树是一种递归数据结构,其中每个节点最多可以有两个子节点。

二叉树的一种常见类型是二叉搜索树,其中每个节点的值都大于或等于左子树中的节点值,并且小于或等于右子树中的节点值。

这是这种二叉树的直观表示:
在这里插入图片描述
实现细节方面,我们将使用一个辅助Node结构体来存储值,并保留对每个孩子的引用:

type T inttype Node struct {val   Tleft  *Noderight *Node
}

然后我们将添加树的起始节点,通常称为根:

type BinSearchTree struct {sync.RWMutexroot *Node
}

常见操作

现在让我们看看我们可以在二叉树上执行的最常见的操作。

插入

我们要介绍的第一个操作是插入新节点。
首先,我们必须找到要添加新节点的位置,以保持树的排序。我们将从根节点开始寻找,遵循这些规则:

  • 如果新节点的值小于当前节点的值,我们去左孩子
  • 如果新节点的值大于当前节点的值,我们去右孩子
  • 如果当前节点为空时,我们到达了一个叶节点,我们可以在该位置插入新节点

然后我们将创建一个递归方法来进行插入:

// insert internal function to find the correct place for a node in a tree
func insert(root, node *Node) {if node.val < root.val {if root.left == nil {root.left = node} else {insert(root.left, node)}} else {if root.right == nil {root.right = node} else {insert(root.right, node)}}
}

接下来我们将创建从根节点开始递归的公共方法:

// Insert inserts the val in the tree
func (bst *BinSearchTree) Insert(val T) {bst.Lock()defer bst.Unlock()node := NewTree(val)if bst.root == nil {bst.root = node} else {insert(bst.root, node)}
}

搜索

现在让我们添加一个方法来检查树是否包含特定值。

和以前一样,我们将首先创建一个遍历树的递归方法:

// search internal recursive function to search t in the tree
func search(root *Node, t T) bool {if root == nil {return false}if root.val == t {return true}if root.val > t {return search(root.left, t)} else {return search(root.right, t)}
}

在这里,我们通过将其与当前节点中的值进行比较来搜索该值;然后,我们将根据结果继续左或右孩子。

接下来我们将创建从根开始的公共方法:

// Search returns true if the t exists in the tree
func (bst *BinSearchTree) Search(t T) bool {bst.RLock()defer bst.RUnlock()return search(bst.root, t)
}

删除

另一种常见的操作是从树中删除一个节点。

首先,我们必须以与之前类似的方式找到要删除的节点:

// remove internal recursive function to remove t
func remove(root *Node, t T) {if root == nil {return}if root.val > t {remove(root.left, t)} else if root.val < t {remove(root.right, t)} else {if root.left == nil && root.right == nil {root = nilreturn} else if root.left == nil {root = root.rightreturn} else if root.right == nil {root = root.leftreturn} else {leftMostRightSide := root.rightfor {//find the smallest value on the right sideif leftMostRightSide != nil && leftMostRightSide.left != nil {leftMostRightSide = leftMostRightSide.left} else {break}}root.val = leftMostRightSide.valremove(root.right, root.val)return}}
}

一旦我们找到要删除的节点,主要有 3 种不同的情况:

  • 没有孩子:这是最简单的情况;我们只需要在它的父节点中用nil替换这个节点
  • 只有一个孩子:在父节点中,我们用它唯一的孩子替换这个节点。
  • 有两个孩子:这是最复杂的情况,因为它需要树重组

最后,我们将创建从根开始删除的公共方法:

// Remove removes the t from the tree
func (bst *BinSearchTree) Remove(t T) {bst.Lock()defer bst.Unlock()remove(bst.root, t)
}

遍历树

在本节中,我们将探索遍历树的不同方法,前序、中序和后序遍历,这里暂时只实现递归的方法。

前序

// PreOrder visits all nodes with pre-order traversing
func (bst *BinSearchTree) PreOrder(f func(T)) {bst.Lock()defer bst.Unlock()preOrder(bst.root, f)
}// preOrder internal recursive function to traverse pre-order
func preOrder(root *Node, f func(T)) {if root != nil {f(root.val)inOrder(root.left, f)inOrder(root.right, f)}
}

中序

// InOrder visits all nodes with in-order traversing
func (bst *BinSearchTree) InOrder(f func(T)) {bst.Lock()defer bst.Unlock()inOrder(bst.root, f)
}// inOrder internal recursive function to traverse in-order
func inOrder(root *Node, f func(T)) {if root != nil {inOrder(root.left, f)f(root.val)inOrder(root.right, f)}
}

后序

// PreOrder visits all nodes with pre-order traversing
func (bst *BinSearchTree) PreOrder(f func(T)) {bst.Lock()defer bst.Unlock()preOrder(bst.root, f)
}// preOrder internal recursive function to traverse pre-order
func preOrder(root *Node, f func(T)) {if root != nil {f(root.val)inOrder(root.left, f)inOrder(root.right, f)}
}

其他

本文还提供了一些其他方法,如:

  • Max()、Min():获取二叉搜索树中最大或最小值
  • Print():打印二叉搜索树
// Min returns the minimal value stored in the tree
func (bst *BinSearchTree) Min() *T {bst.RLock()defer bst.RUnlock()root := bst.rootif root == nil {return nil}for {if root.left == nil {return &root.val}root = root.left}
}// Max returns the maximal value stored in the tree
func (bst *BinSearchTree) Max() *T {bst.RLock()defer bst.RUnlock()root := bst.rootif root == nil {return nil}for {if root.right == nil {return &root.val}root = root.right}
}func (bst *BinSearchTree) Print() {bst.Lock()defer bst.Unlock()fmt.Println("------------------------------------------------")stringify(bst.root, 0)fmt.Println("------------------------------------------------")
}func stringify(root *Node, level int) {if root != nil {format := ""for i := 0; i < level; i++ {format += "       "}format += "---[ "level++stringify(root.left, level)fmt.Printf(format+"%d\n", root.val)stringify(root.right, level)}
}

单元测试

import ("testing"
)const (t1 T = iota + 1t2t3t4t5t6t7t8t9t10t11
)func InitTree() *BinSearchTree {bst := NewBinSearchTree()bst.Insert(t3)bst.Insert(t2)bst.Insert(t4)bst.Insert(t9)bst.Insert(t5)bst.Insert(t6)bst.Insert(t10)bst.Insert(t1)bst.Insert(t7)bst.Insert(t8)return bst
}func TestBinSearchTree_Insert(t *testing.T) {bst := InitTree()bst.Print()bst.Insert(t11)bst.Print()
}func TestBinSearchTree_InOrder(t *testing.T) {var ret []Tbst := InitTree()bst.InOrder(func(t T) {ret = append(ret, t)})expected := []T{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}if !isSameSlice(ret, expected) {t.Errorf("Traversal order incorrect, got %v", ret)}
}// isSameSlice returns true if the 2 slices are identical
func isSameSlice(a, b []T) 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
}

部分测试结果:

=== RUN   TestBinSearchTree_Insert
---------------------------------------------------[ 1---[ 2
---[ 3---[ 4---[ 5---[ 6---[ 7---[ 8---[ 9---[ 10
------------------------------------------------
---------------------------------------------------[ 1---[ 2
---[ 3---[ 4---[ 5---[ 6---[ 7---[ 8---[ 9---[ 10---[ 11
------------------------------------------------
--- PASS: TestBinSearchTree_Insert (0.00s)
PASS

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/784987.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unbtun-arach64架构安装PySide2(python3.6)

aarch平台是无法通过pip安装PySide2的&#xff0c;同时利用源码下载一直报错 1. 我是python3.6.9&#xff0c;在官网上找到对应的PySide2版本 5.15.2.所以首先在官网下载Qt5.15.2的源码&#xff1a;https://download.qt.io/archive/qt/5.15/5.15.2/single/ 2. 编译qt环境 aar…

Seata(分布式事务实例环境搭建)

文章目录 1.基本介绍1.引出Seata2.问题分析 2.Seata的安装和配置1.解压到d盘2.修改D:\seata\conf\file.conf文件1.修改事务组2.修改日志存储模式为db3.修改数据库&#xff08;MySQL5.7&#xff09;连接信息4.创建seata数据库5.复制db_store.sql的内容&#xff0c;创建需要的表6…

Web实例_报表开发01-基于HTML进行报表呈现

Web实例_报表开发01-基于HTML进行报表呈现 报表开发是一种在利用了软件的基础上, 针对不同类型的报表, 进行开放的工作。 而以报表的方式, 将相关的内容、数值呈现出来的话, 则会起到更好的概况作用。 再加上, 报表开发工作是依托于计算机来完成的, 因此在效率、完整性等方面…

红酒:分类视角下的红酒品质评估与标准制定

在红酒的世界中&#xff0c;品质的评估与标准的制定对于维护消费者权益、促进行业健康发展具有重要意义。云仓酒庄雷盛红酒作为业界持续发展品牌&#xff0c;从分类的视角出发&#xff0c;对红酒品质进行了多方的评估&#xff0c;并积极参与制定相关的标准。 首先&#xff0c;从…

优思学院|工程经理应如何利用PDCA循环?

作为工程经理&#xff0c;理解PDCA&#xff08;计划-执行-检查-行动&#xff09;质量管理循环程序对于确保项目质量和持续改进是十分重要。 PDCA 最早由美国质量管理专家 Walter A. Shewhart (1939)提出&#xff0c;其后被戴明博士&#xff08;Dr. Deming&#xff09;所采用、…

【Linux】权限理解

权限理解 1. shell命令以及运行原理2. Linux权限的概念3. Linux权限管理3.1 文件访问者的分类&#xff08;人&#xff09;3.2 文件类型和访问权限&#xff08;事物属性&#xff09;3.2.1 文件类型3.2.2 基本权限 3.3 文件权限值的表示方法3.4 文件访问权限的相关设置方法3.4.1 …

基于energy score的out-of-distribution数据检测,LeCun都说好 | NerulPS 2020

论文提出用于out-of-distributions输入检测的energy-based方案&#xff0c;通过非概率的energy score区分in-distribution数据和out-of-distribution数据。不同于softmax置信度&#xff0c;energy score能够对齐输入数据的密度&#xff0c;提升OOD检测的准确率&#xff0c;对算…

【JAVA】java基础(分支结构)洛谷刷题(含图解:吃苹果)

题目&#xff1a; 解析 代码 import java.util.Scanner;public class Main {public static void main(String[] args) {// TODO Auto-generated method stubScanner input new Scanner(System.in);int apple,time,rate; appleinput.nextInt(); rateinput.nextInt(); timeinpu…

专题【链表】刷题日记

题目列表 学习题(22题) 2024.03.31 两数相加 19. 删除链表的倒数第 N 个结点 合并K个升序链表 2024.04.01 24. 两两交换链表中的节点 25. K 个一组翻转链表 61. 旋转链表 83. 删除排序链表中的重复元素 82. 删除排序链表中的重复元素 II 86. 分隔链表 92. 反转链表 II 1…

【STM32嵌入式系统设计与开发】——14PWM(pwm脉宽输入应用)

这里写目录标题 一、任务描述二、任务实施1、WWDG工程文件夹创建2、函数编辑&#xff08;1&#xff09;主函数编辑&#xff08;2&#xff09;USART1初始化函数(usart1_init())&#xff08;3&#xff09;USART数据发送函数&#xff08; USART1_Send_Data&#xff08;&#xff09…

服务器安全事件应急响应排查方法

针对服务器操作系统的安全事件也非常多的。攻击方式主要是弱口令攻击、远程溢出攻击及其他应用漏洞攻击等。分析安全事件&#xff0c;找到入侵源&#xff0c;修复漏洞&#xff0c;总结经验&#xff0c;避免再次出现安全事件&#xff0c;以下是参考网络上文章&#xff0c;总结的…

初识编译和链接(C语言)

文章目录 编译和链接翻译环境预处理编译汇编链接 运行环境 编译和链接 编译和链接这两个大的过程构成了翻译环境。 其实&#xff0c;在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。 一个环境是翻译环境&#xff0c;另一个是执行环境。 翻译环境中&#xff0c;源…

【软件工程】详细设计(一)

1. 引言 1.1 编写目的 该文档的目的是描述《学生成绩管理系统》项目的详细设计&#xff0c;其主要内容包括&#xff1a; 系统功能简介 系统详细设计简述 各个模块的实现逻辑 最小模块组件的伪代码 本文档的预期的读者是&#xff1a; 开发人员 项目管理人员 测试人员 …

基于单片机汽车超声波防盗系统设计

**单片机设计介绍&#xff0c;基于单片机汽车超声波防盗系统设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机汽车超声波防盗系统设计概要主要涉及利用超声波传感器和单片机技术来实现汽车的安全防盗功能。以下是对…

八股 -- C#

面向对象 &#xff08;三大特性&#xff09; 三大特性目的是为了提供更好的代码组织、可维护性、扩展性和重用性 C#基础——面向对象 - 知乎 (zhihu.com) 封装 理解&#xff1a; 你不需要了解这个方法里面写了什么代码&#xff0c;你只需要了解这个方法能够给你返回什么数据&…

位运算 ----力扣2220

2220. 转换数字的最少位翻转次数 提示 一次 位翻转 定义为将数字 x 二进制中的一个位进行 翻转 操作&#xff0c;即将 0 变成 1 &#xff0c;或者将 1 变成 0 。 比方说&#xff0c;x 7 &#xff0c;二进制表示为 111 &#xff0c;我们可以选择任意一个位&#xff08;包含没有…

书生·浦语大模型-第一节课笔记

视频总结 23年发布的模型在一些材料中归位指令微调模型&#xff0c;后面逐渐升级应该已经是train的模型了 技术报告总结 InternLM2 Technical Report 评测与特点 6 dimensions and 30 benchmarks, long-context modeling, and open-ended subjective evaluations长文本…

猜数游戏(Python)

一、实验要求&#xff1a; &#xff08;1&#xff09;在游戏开始时&#xff0c;随机生成一个1~100之间的整数。 &#xff08;2&#xff09;在游戏中&#xff0c;玩家有10次机会猜数。如果10次都没有猜中&#xff0c;则游戏失败&#xff1b;否则&#xff0c;游戏成功。 &…

centos7.5安装gitlab-runner,配置CI/CD流水线

一般不建议gitlab-server和gitlab-runner装在同一台服务器 第一步&#xff1a;安装gitlab-runner,最好和gitlab实例版本一致 # 下载官方gitlab-runner安装脚本 curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | s…

成为一名自由程序员

越来越多的程序员已经或希望加入到自由职业者的行列&#xff0c;你是否也是其中的一员呢&#xff1f;在这篇文章里我将尝试结合自身的一些经验&#xff0c;来告诉你成为自由程序员能够获得的那些益处&#xff0c;以及为了取得成功所需要面临的挑战。 自由程序员的类型 很多人把…