Go版数据结构 -【4.2 二叉搜索树】

4.2 二叉搜索树

二叉搜索树(Binary Search Tree,简称 BST)是一种特殊的二叉树结构,它在上一节的基础上做了新的规则限制。

它的特性使得在进行查找、插入、删除等操作时效率非常高。二叉搜索树广泛应用于搜索、排序和数据存储等场景。

本节我们将介绍二叉搜索树的基本概念、其特有的规则以及在 Go 语言中的实现。

本节代码存放目录为 lesson7

概念及原理

二叉搜索树(BST) 是一种有序的二叉树,其每个节点都有以下特性:

  • 左子树节点的值 小于 父节点的值

  • 右子树节点的值 大于 父节点的值

  • 对于每个节点,左子树和右子树 本身也都是二叉搜索树。

这样的性质使得我们可以在二叉搜索树上快速进行查找、插入、删除操作。


二叉搜索树的结构示意图如下所示:

        8
       / \
      3   10
     / \    \
    1   6   14
       / \   /
      4   7 13
  • 节点 8 是根节点,左子树上的所有节点都小于8,右子树上的所有节点都大于8

  • 节点 38的左子节点,节点 108的右子节点。

  • 节点 1、6、14、4、7、13 也是遵循二叉搜索树的性质,左子节点小于父节点,右子节点大于父节点。

总的来说,对于二叉搜索树,我们只需要记住:左子小于父,右子大于父。


二叉搜索树具有以下几个重要特性:

  • 查找操作:查找某个节点的过程类似于二分查找。我们根据值的大小决定是否向左子树或右子树递归查找,这样可以快速缩小查找范围。

  • 插入操作:插入一个新值时,我们根据该值与当前节点的比较结果,递归找到合适的空位插入,确保二叉搜索树的有序性。

  • 删除操作:删除节点时有三种情况:

    • 节点没有子节点:直接删除该节点。

    • 节点有一个子节点:删除节点后,直接将其子节点连接到父节点。

    • 节点有两个子节点:找到该节点的中序后继节点,替换到被删除节点的位置,再递归删除后继节点。


与普通二叉树一样,二叉搜索树也可以使用前序遍历中序遍历后序遍历层序遍历。特别是中序遍历,它会按照升序输出节点的值。

以上面的树为例,使用 中序遍历 的顺序就是从小到大依次输出节点值:

1 -> 3 -> 4 -> 6 -> 7 -> 8 -> 10 -> 13 -> 14

如何查找中序后继节点?

我么可以简单理解为:采用中序遍历后,第一个比当前节点大的节点。

比如在上面的中序遍历后,我们得到:

1 -> 3 -> 4 -> 6 -> 7 -> 8 -> 10 -> 13 -> 14

那么1的后继就是3,因为3是第一个比1大的;同理6的后继就是77的后继就是8

删除操作示例

我们同样以上文的例子示范:

        8
       / \
      3   10
     / \    \
    1   6   14
       / \   /
      4   7 13

首先我们删除13,由于13是没有任何子节点的,所以我们直接将13节点删除即可。此时结构如下:

        8
       / \
      3   10
     / \    \
    1   6   14
       / \ 
      4   7

接下来我们删除10节点,由于10节点只有一个节点14,所以我们直接将14连接到父节点8即可。此时结构如下:

        8
       / \
      3   14
     / \   
    1   6
       / \ 
      4   7

接下来我们删除6节点,由于6节点有两个子节点,所以我们需要找到6的中序后继节点,也就是7,之后我们将7替换到被删除的6节点。此时结构如下:

        8
       / \
      3   14
     / \   
    1   7
       / \ 
      4   7

接下来我们需要删除原本的中序后继节点7。最终结构如下:

        8
       / \
      3   14
     / \   
    1   6
       /
      4

Go语言的实现

接下来我们使用 Go 语言实现二叉搜索树,包括插入、查找和删除等基本操作。

实现代码如下所示:

// Tree 定义树结构
type Tree struct {
	data  int
	left  *Tree
	right *Tree
}

func (t *Tree) insert(data int) {
	newTree := &Tree{
		data: data,
	}

	if data < t.data {
		if t.left == nil {
			t.left = newTree
		} else {
			t.left.insert(data)
		}
	} else {
		if t.right == nil {
			t.right = newTree
		} else {
			t.right.insert(data)
		}
	}
}

func (t *Tree) search(data int) *Tree {
	if t == nil {
		return nil
	}

	if t.data == data {
		return t
	}

	// 递归查找左子树
	if data < t.data {
		return t.left.search(data)
	}
	// 递归查找右子树
	return t.right.search(data)
}

func (t *Tree) delete(data int) *Tree {
	if t == nil {
		return nil
	}

	if data < t.data {
		// 递归左查找
		t.left = t.left.delete(data)
	} else if data > t.data {
		// 递归右查找
		t.right = t.right.delete(data)
	} else {
		// 没有任何子节点
		if t.left == nil && t.right == nil {
			return nil
		}

		// 只有一个子节点
		if t.left == nil {
			return t.right
		}

		if t.right == nil {
			return t.left
		}

		// 有两个子节点,那么需要找到中序后继节点
		minNode := t.right.findMin()
		// 用中序后继的值替换当前节点的值
		t.data = minNode.data
		// 删除中序后继节点
		t.right = t.right.delete(minNode.data)
	}
	return t
}

// 查找最小节点(中序后继)
func (t *Tree) findMin() *Tree {
	if t.left == nil {
		return t
	}
	return t.left.findMin()
}

// 中序遍历
func (t *Tree) inOrder() {
	if t == nil {
		return
	}
	t.left.inOrder()
	fmt.Printf("%d ", t.data)
	t.right.inOrder()
}

func (t *Tree) printTree() {
	if t == nil {
		return
	}
	if t.left != nil {
		fmt.Printf("left: %d, ", t.left.data)
	}
	if t.right != nil {
		fmt.Printf("right-> %d\n", t.right.data)
	}
	if t.left == nil && t.right == nil {
		return
	}
	t.left.printTree()
	t.right.printTree()
}

func main() {
	tree := &Tree{data: 8}
	tree.insert(3)
	tree.insert(10)
	tree.insert(1)
	tree.insert(6)
	tree.insert(14)
	tree.insert(4)
	tree.insert(7)
	tree.insert(13)
	tree.printTree()
	fmt.Println("\n中序遍历结果:")
	tree.inOrder()

	fmt.Println("\n\n查找节点 6")
	node := tree.search(6)
	if node != nil {
		fmt.Printf("找到节点: %d\n", node.data)
	} else {
		fmt.Println("节点不存在")
	}

	fmt.Println("\n删除节点 13 后的中序遍历结果:")
	tree = tree.delete(13)
	tree.inOrder()

	fmt.Println("\n删除节点 10 后的中序遍历结果:")
	tree = tree.delete(10)
	tree.inOrder()

	fmt.Println("\n删除节点 6 后的中序遍历结果:")
	tree = tree.delete(6)
	tree.inOrder()
}

执行结果输出如下:

left: 3, right-> 10
left: 1, right-> 6
left: 4, right-> 7
right-> 14
left: 13, 
中序遍历结果:
1 3 4 6 7 8 10 13 14 

查找节点 6
找到节点: 6

删除节点 13 后的中序遍历结果:
1 3 4 6 7 8 10 14 
删除节点 10 后的中序遍历结果:
1 3 4 6 7 8 14 
删除节点 6 后的中序遍历结果:
1 3 4 7 8 14

在上面的代码中我们实现了基本的插入、查询、删除、遍历,可能代码看起来不是太明白,我们可以先将递归了解清楚,我们主要使用的也就是递归的概念。

通过递归进行各种操作,所以代码看起来会比较抽象,我们可以拿到代码demo,去加一下日志输出详细查看一下他的整个过程。

小结

二叉搜索树是二叉树的一种扩展形式,具有排序和快速查找的优势。通过本节的学习,我们了解了二叉搜索树的特性和操作实现。

下一节我们将继续深入学习AVL树,它们在保持二叉搜索树性能的同时,通过自平衡机制进一步优化了查找和插入的效率。


我的GitHub:https://github.com/swxctx

书籍地址:https://gs.golang.website/

书籍代码:https://github.com/YouCanGolang/GoStructedCode

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CSS 浏览器兼容问题探讨

目录 非 VIP 用户可前往公众号回复“css”进行免费阅读 浏览器介绍 css 选择器兼容介绍 ie6 微型盒子兼容解决方法 ie6双倍margin div中放入一个img元素导致div高度多出几像素 非 VIP 用户可前往公众号回复“css”进行免费阅读 浏览器介绍 在国内,常见的网页浏览…

华为认证HCIA篇--网络通信基础

大家好呀&#xff01;我是reload。今天来带大家学习一下华为认证ia篇的网络通信基础部分&#xff0c;偏重一些基础的认识和概念性的东西。如果对网络通信熟悉的小伙伴可以选择跳过&#xff0c;如果是新手或小白的话建议还是看一看&#xff0c;先有个印象&#xff0c;好为后续的…

机器学习:opencv--特征检测

目录 前言 一、 Harris 角点检测 1.基本思想 2.代码实现 二、 SIFT&#xff08;尺度不变特征变换&#xff09; 1.代码实现 前言 特征检测是计算机视觉中的一个重要任务&#xff0c;旨在从图像中提取具有辨识度的关键点或区域。这些特征可以用于后续的图像分析、匹配和识别…

25维谛技术面试最常见问题面试经验分享总结(包含一二三面题目+答案)

开头附上工作招聘面试必备问题噢~~包括综合面试题、无领导小组面试题资源文件免费&#xff01;全文干货。 【免费】25维谛技术面试最常见问题面试经验分享总结&#xff08;包含一二三面题目答案&#xff09;资源-CSDN文库https://download.csdn.net/download/m0_72216164/8979…

设计模式之策略设计模式

一、状态设计模式概念 策略模式&#xff08;Strategy&#xff09; 是一种行为设计模式&#xff0c; 它能让你定义一系列算法&#xff0c; 并将每种算法分别放入独立的类中&#xff0c; 以使算法的对象能够相互替换。 适用场景 当你想使用对象中各种不同的算法变体&#xff0c; …

【HTTP 和 HTTPS详解】3

HTTP 状态代码 HTTP 状态代码是服务器发送给客户端的三位数字&#xff0c;用于指示客户端请求的结果。它们分为五类&#xff1a;信息性&#xff08;100-199&#xff09;、成功&#xff08;200-299&#xff09;、重定向&#xff08;300-399&#xff09;、客户端错误&#xff08…

算法宝典——二分查找算法

1.认识二分查找 二分查找的时间复杂度:O(logN) 二分查找属于算法中耳熟能详的一类&#xff0c;通常的我们会说只有数组有序才可以使用二分查找&#xff0c;不过这种说法并不完全正确&#xff0c;只要数据具有"二段性"就可以使用二分查找&#xff0c;即我们可以找出一…

贪心的思想

803.区间合并 给定 n 个区间 [li,ri]&#xff0c;要求合并所有有交集的区间。 注意如果在端点处相交&#xff0c;也算有交集。 输出合并完成后的区间个数。 例如&#xff1a;[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。 输入格式 第一行包含整数 n。 接下来 n 行&#x…

Unity中的功能解释(数学位置相关和事件)

向量计算 Vector3.Slerp&#xff08;起点坐标&#xff0c;终点坐标&#xff0c;t&#xff09;&#xff0c;可是从起点坐标以一个圆形轨迹到终点坐标&#xff0c;有那么多条轨迹&#xff0c;那怎么办 Vector3.Slerp 进行的是沿球面插值&#xff0c;因此并不是沿着严格的“圆形…

【CSS】盒子模型

width 宽度 、height 高度 、padding 内边距 、margin 外边距 ( 外边距合并、子元素外边距塌陷问题 )border 边框border-radius 圆角box-shadow 阴影overflow 溢出float 浮动 ( 父元素塌陷问题 ) 盒子模型&#xff08;Box Model &#xff09;是指在网页设计中&#xff0c;用于描…

Linux云计算 |【第四阶段】RDBMS1-DAY2

主要内容&#xff1a; 常用函数&#xff08;函数分类1&#xff1a;单行、分组&#xff1b;函数分类2&#xff1a;字符、数学、日期、流程控制&#xff09;、分组查询group by、连接查询 一、常用函数 1. 按使用方式分类 ① 单行函数 单行函数&#xff08;Scalar Functions&…

老古董Lisp实用主义入门教程(12):白日梦先生的白日梦

白日梦先生的白日梦 白日梦先生已经跟着大家一起学Lisp长达两个月零五天&#xff01; 001 粗鲁先生Lisp再出发002 懒惰先生的Lisp开发流程003 颠倒先生的数学表达式004 完美先生的完美Lisp005 好奇先生用Lisp来探索Lisp006 好奇先生在Lisp的花园里挖呀挖呀挖007 挑剔先生给出…

构建高可用和高防御力的云服务架构第二部分:SLB负载均衡(2/5)

在现代云服务中&#xff0c;负载均衡&#xff08;Load Balancing&#xff09;是一种关键技术&#xff0c;用于优化资源利用、最小化响应时间、提高系统的可伸缩性和可靠性。负载均衡器位于客户端和服务器之间&#xff0c;根据预设的策略将请求分发到多个服务器上&#xff0c;以…

如何使用ssm实现基于web的山东红色旅游信息管理系统的设计与实现

TOC ssm716基于web的山东红色旅游信息管理系统的设计与实现jsp 绪论 1.1研究背景 从古到今&#xff0c;信息的录入&#xff0c;存储&#xff0c;检索都受制于社会生产力的发展&#xff0c;不仅仅浪费大量的人力资源还需要浪费大量的社会物资&#xff0c;并且不能长时间的保…

计算机前沿技术-人工智能算法-生成对抗网络-算法原理及应用实践

计算机前沿技术-人工智能算法-生成对抗网络-算法原理及应用实践 1. 什么是生成对抗网络&#xff1f; 生成对抗网络&#xff08;Generative Adversarial Networks&#xff0c;简称GANs&#xff09;是由Ian Goodfellow等人在2014年提出的一种深度学习模型&#xff0c;主要用于数…

JavaEE: 深入探索TCP网络编程的奇妙世界(三)

文章目录 TCP核心机制TCP核心机制三: 连接管理建立连接(三次握手)断开连接(四次挥手)三次握手/四次挥手 流程简图 TCP核心机制 前一篇文章 JavaEE: 深入探索TCP网络编程的奇妙世界(二) 书接上文~ TCP核心机制三: 连接管理 建立连接(三次握手),断开连接(四次挥手). 这里的次数…

二叉树的前序遍历,中序遍历,后序遍历(非递归方法+C语言代码)

#include<stdlib.h> #include<stdio.h> #include<assert.h> #include<stdbool.h> //定义一个二叉树结点结构体 typedef int ElemTpye; typedef struct TreeNode {ElemTpye data;struct TreeNode* left;struct TreeNode* right; }TreeNode; //创建结点 …

【中间件——基于消息中间件的分布式系统的架构】

1. 基于消息中间件的分布式系统的架构 从上图中可以看出来&#xff0c;消息中间件的是 1&#xff1a;利用可靠的消息传递机制进行系统和系统直接的通讯 2&#xff1a;通过提供消息传递和消息的排队机制&#xff0c;它可以在分布式系统环境下扩展进程间的通讯。 1.1 消息中间件…

影视站群程序大对比,苹果cmsv10 vs海洋cms

在影视站群程序领域&#xff0c;苹果CMSv10和海洋CMS是两款备受站长们青睐的程序。它们分别具备各自的优势&#xff0c;适合不同需求的站群管理和优化。以下是两者的详细对比&#xff0c;并重点介绍苹果CMS的主要优势和插件功能。 苹果CMSv10简介 maccmscn 苹果CMSv10&#x…