跳过正文
  1. 博客/
  2. 随笔/
  3. 编程/

二叉搜索树实现原理

·4 分钟· ·
随笔 编程
目录

本篇博客主要基于这篇博客的扩展,建议阅读前先阅读这篇博文,这篇博文详细介绍二叉搜索树的实现原理,完整代码在githubbinary.go文件中

浅谈"树"这种数据结构
Github
可视化页面

引言
#

之所以使用Go来实现,个人还是比较喜欢Go的,作为一个基础数据结构,Go用来实现这个速度比Java、C++都快,而且相比Java也能节省内存,而且我也不喜欢换使用冒号。

二叉搜索树的基本功能
#

作为一个树结构,主要必须实现三个功能,查增删(当然有改,不过改是删和增的结合所以就不算在里面),当然二叉树还能帮我实现一些额外功能,比如寻找最大值最小值等等,实现一颗二叉搜索树必须要保证在“增”和“删”的时候保持树结构不变,也就是保证还是一颗二叉搜索树

什么二叉树是二叉搜索树呢,二叉搜索树只需要保证一点

  • 任何一个节点,它的左子树(不为空)所有值小于这个节点值,它的右子树(不为空)所有值大于这个节点值

所有我们在实现增删的时候必须要牢记这点,而且我们也可以知道在你开始对这颗二叉树进行增删的时候,它一定已经满足了上面这个条件

#

标准的二叉树

如何在上面这颗标准的二叉树里面查询到我们想要的值呢?我们知道一颗二叉树只需要知道根节点(上面的10)就能通过遍历得到所以的节点的值,所以我们在实现中声明一个二叉搜索树的时候只保存一个root节点,通过这个节点,我们就可以实现所以的增删查改功能了

为了找到每个值a,我们依次要在每个节点上对值进行比较,因为每个节点的值代表已经将这堆数据分成两个部分,一堆比这个值大在右边,一堆比这个值小在左边,这样只要最终能找到一个那个值的最小区间的对应值(找到)或者找到一个为空的叶节点(代表没有找到),

通过最简单的查找,我们可以反推出一个理论,假如我们需要插入一个值,我们只要先尝试去寻找到那个值,假如找到了,因为已经存贮了就不插入,假如没找到就会找到一个为空的叶子节点,那样只要在父节点将这个为空的叶子节点替换成这个值为a的节点,我们就完成了插入

所以查找和插入是相对的,知道怎么查找就知道怎么插入,接下来我们详细介绍如何删除,这个非常关键,因为它是后面要介绍的高级树实现高效的删除的关键

删除
#

我们还是以上面那种图举例子,删除我们最先想到的是删除12,17那些节点,直接删除掉对二叉树没有什么影响,但是如果我们要删除那些带有节点的呢?我们先简化问题,先看一个只有两个节点的树

双节点树

我们要想删除10,有两种可能

左移

右移

一种是将左边值移过来,一种是将右边值移过来,这两种都不会破坏二叉树的平衡,现在我们假设我们倾向将左边的值移过来,我们再回到上面那棵标准的二叉树,如果我们要删除掉10节点,我们要把那个值移过来,我们直接在可视化界面进行这个操作

删除10后的标准二叉树

我们发现,我们只需要将7和放到10的位置上就完成了一次删除的操作,而7与10的关系是,7是10左边的那堆值最大的,用一个通俗的话来说,完成改朝换代的关键就是要找一个能镇的住手下的人,由于“10”挂了,在左边只有“7”能镇住左边,所以"7”升官直接跑到“10”的位置了

根据这个原则我们将情况分成三种

  1. 左右子树都为空,直接删掉
  2. 左边子树或者右边子树有一个为空,将不为空的子树提上来当做删除的节点
  3. 左右子树不为空,将左边最大的值提上来替换删除的节点

对于第三种情况,为了编程方便,我们考虑一种情况,如果左子树的右节点有没有值(比如说上面的5节点),如果它右节点有值,那么左边的最大值肯定出现在,左子树的右节点上(就像上面左子树出现在5的右节点的7节点上面)。然后我们继续判断“7”节点有没有右子树,这样循环下去,我们总能找到一个节点没有右节点,这样我们就找到左子树的最大值了。

在上面这种情况下,我们将这情况分成两种

  • 如果左子树的右节点有值(循环下去找到一个节点右节点没有值)
  • 如果左子树的右节点没值(那么第一个左子树就是最大值)

具体的代码binary.goDelete(寻找节点)方法和delete(删除节点)方法中

总结
#

总的来说,二叉搜索树的插入是最容易理解的,但是删除的话要考虑不同四种的情况所以还是稍微需要点时间理解一下的,小小的一个二叉搜索树也花了近200行代码实现,但是相比后面的AVL树、红黑树的近千行代码来说,二叉搜索树还是比较简单的,而且后面实现的高级树结构基本上都是依赖二叉搜索树的实现(必须要按照二叉树的增删满足二叉搜索树的原则),所以我们对二叉搜索树的增删必须理解透彻。

引用:

https://en.wikipedia.org/wiki/Binary_search_tree

相关文章

AVL树实现原理
·2 分钟
随笔 编程
本篇博客主要基于这篇博客的扩展,建议阅读前先阅读这篇博文,这篇博文详细介绍AVL树的实现原理,完整代码在github的avl.go文件中 浅谈"树"这种数据结构 Github 可视化页面 引言 # AVL树是在对二叉搜索树的一种优化,通过构造一棵高度平衡的二叉搜索树从而实现提高空间利用率,所以在了解如何实现之前,必须了解如何构造一棵二叉搜索树,你可以阅读我的这篇博客了解如何构建一棵二叉搜索树,虽然我是用Go来实现的,但是不必了解太多Go方面的知识,我在博客中尽量使用图片的方式来介绍实现原理
从问题理解动态规划
·5 分钟
随笔 编程
网上关于动态规划的资料,大部分直接给结论,所以一开始我一头雾水,搞不懂为什么要这么做,这篇博文就从实际问题出发,简单的剖析动态规划
浅谈"树"这种数据结构
·10 分钟
随笔 编程
一直以来我对树这种数据结构就比较头疼,随便找一个红黑树的博客,大部分都是在谈怎么旋转怎么插入怎么删除,将算法讲的头头是道,但是就算你看懂了也不懂为什么要这样做,所以我们这篇博文就从可视化的角度,慢慢的介绍这些树的来世今生。
几个有趣的动态规划
·6 分钟
随笔 编程
这篇博文是从问题理解动态规划的练习篇,通过几个动态规划的问题剖析进一步理解动态规划 找零钱练习题 # 给定一个零钱数组比如[1, 2, 5],每个值代表一个面额的纸币,给定一个总数(aim),求换钱有多少种方法(每种面额纸币不限量) 这个问题非常经典,所以我就从最先容易想到的算法出发慢慢推导出动态规划
js的this引发的思考
·2 分钟
随笔 编程
最近这几天在开发一个hmtl5的游戏, 但是对于js怎么使用面对对象来编程有点困惑,查了一些资料 整理如下.
漫谈排序算法
·4 分钟
随笔 编程
0x00 引子 # 排序是很多算法的基础,简简单单的排序前人就归纳出很多种算法,但是这些算法多多少少都有着相同的原理