二叉搜索树

二叉搜索树是一种特殊的二叉树,它的特点是:

  • 对于任意一个节点p,存储在p的左子树的中的所有节点中的值都小于p中的值

  • 对于任意一个节点p,存储在p的右子树的中的所有节点中的值都大于p中的值

一个图例:

基于二叉搜索树的这种关系,我们可以用它来实现有序映射

遍历二叉搜索树

基于二叉搜索树的特性,采用中序遍历的方式可以使得遍历结果是按照从小到大的顺序排列的。了解中序遍历可以参考用Python实现数据结构之树

这里还需要思考的一个内容是在基于中序遍历的前提下,如何求一个节点的后继节点或前驱节点。

显然这就是求比该节点大的下一个节点或比它小的前一个节点。我们拿求后继节点为例:

  • 当该节点有右孩子时,那么后继结点就是右子树中最左边的节点

  • 当该节点没有右孩子时,那么后继节点就是第一个是左孩子的祖先的父节点

算法用伪代码表示为:


def after(p):
"""寻找二叉搜索树的后继节点的伪代码""" if right(p) is not None:
walk = right(p)
while left(right(p)) is not None: # 找最左
walk = left(walk)
return walk
else:
walk = p
ancestor = parent(walk)
while ancestor is not None and walk == right(ancestor): # 当walk是左孩子时或walk是根节点时停止
walk = ancestor
ancestor = parent(walk)
return ancestor

找前驱同理

搜索

既然叫做二叉搜索树,那它很重要的一个用途就是搜索,搜索的方式为:

  • 与根节点比较,如果相等则根节点就是要搜索的位置

  • 比根节点小,则递归的与左孩子比较

  • 比根节点大,则递归的与有孩子比较

  • 如果最后没找到,则返回最后一个与之比较的节点,意味着可以在这个节点位置插入刚才搜索的值

算法用伪代码表示为:


def search(T,p,k):
"""二叉树搜索的伪代码,k是要搜索的值"""
if k == p.key():
return p
elif k < p.key() and T.left(p) is not None:
return search(T,T.left(p))
elif k > p.key() and T.right(p) is not None:
return search(T,T.right(p))
return p

搜索的时间与高度有关,是O(h),也就是最坏的情况下为O(n),最好的情况下是O(log(n))

插入

插入算法较简单,它依赖于搜索算法,将搜索的返回的位置的值与key进行比较,

  • 如果相等,则就在此位置修改

  • 如果小于返回位置的值,则插入到返回位置的左孩子

  • 如果小于返回位置的值,则插入到返回位置的右孩子

删除

删除操作较为复杂,因为删除的位置可以是任意的位置,设删除的位置为p

  • 如果删除的位置没有孩子,则直接删了就行

  • 如果删除的位置有一个孩子,则删除之后把它的孩子接到它原来的位置上

  • 如果删除的位置有两个孩子,则:

1.先找到位置p的前驱r,前驱在左子树中

2.把p删除,将r代替p

3.把r原来的位置删除

使用前驱的原因是它必然比p的右子树的所有节点小,也必然比除了r的p的左子树的所有节点大

python实现

我们利用二叉树来实现有序映射


class OrderedMap(BinaryTree,MutableMapping):
"""使用二叉搜索树实现的有序映射""" class _item(): def __init__(self, key, value):
self.key = key
self.value = value def __eq__(self, other):
return self.key == other.key def __ne__(self, other):
return self.key != other.key def __lt__(self, other):
return self.key < other.key class Position(BinaryTree.Position): def key(self):
return self.element().key def value(self):
return self.element().value

BinaryTree是在之前文章中定义的二叉树类,具体参考用Python实现数据结构之树

首先定义了两个内嵌类,一个表示键值项,一个用于封装节点

然后定义些非公开方法用于其他方法使用:


def _subtree_search(self, p, k):
"""搜索算法"""
if k == p.key():
return p
elif k < p.key():
if self.left(p) is not None:
return self._subtree_search(self.left(p), k)
else:
if self.right(p) is not None:
return self._subtree_search(self.right(p), k)
return p def _subtree_first_position(self, p):
"""返回树的最左节点"""
walk = p
while self.left(walk) is not None:
walk = self.left(walk)
return walk def _subtree_last_position(self, p):
"""返回树的最右节点"""
walk = p
while self.right(walk) is not None:
walk = self.right(walk)
return walk

下面是一些公开的访问方法:


def first(self):
return self._subtree_first_position(
self.root()) if len(self) > 0 else None def last(self):
return self._subtree_last_position(
self.root()) if len(self) > 0 else None def before(self, p):
"""前驱位置"""
if self.left(p):
return self._subtree_last_position(self.left(p))
else:
walk = p
above = self.parent(walk)
while above is not None and walk == self.left(above):
walk = above
above = self.parent(walk)
return above def after(self, p):
"""后继位置"""
if self.right(p):
return self._subtree_first_position(self.right(p))
else:
walk = p
above = self.parent(walk)
while above is not None and walk == self.right(above):
walk = above
above = self.parent(walk)
return above def find_position(self,k):
if self.is_empty():
return None
else:
p = self._subtree_search(self.root(),k)
return p

接下来是映射的核心方法:


def __getitem__(self, k):
if self.is_empty():
raise KeyError('Key Error'+repr(k))
else:
p = self._subtree_search(self.root(),k)
if k!=p.key():
raise KeyError('Key Error' + repr(k))
return p.value() def __setitem__(self, k,v):
if self.is_empty():
leaf = self.add_root(self._Item(k,v))
else:
p = self._subtree_search(self.root(),k)
if p.key() == k:
p.element().value = v
return
else:
item = self._Item(k,v)
if p.key() < k:
leaf = self.add_right(p,item)
else:
leaf = self.add_left(p, item) def __iter__(self):
p = self.first()
while p is not None:
yield p.key()
p = self.after(p) def mapdelete(self,p):
if self.left(p) and self.right(p): #两个孩子都有的时候
replacement = self._subtree_last_position(self.left(p)) #用左子树最右位置代替
self.replace(p,replacement.element())
p = replacement
self.delete(p) def __delitem__(self, k):
if not self.is_empty():
p = self._subtree_search(self.root(),k)
if k == p.key():
self.mapdelete(p)
return
raise KeyError('Key Error' + repr(k))

最后是一些有序映射特有的方法:


def find_min(self):
"""找最小值,返回键值元组"""
if self.is_empty():
return None
else:
p = self.first()
return(p.key(), p.value()) def find_ge(self, k):
"""找第一个大于等于k的键值元组"""
if self.is_empty():
return None
else:
p = self.find_position(k)
if p.key() < k:
p = self.after(p)
return (p.key(), p.value()) if p is not None else None def find_range(self, start, stop): if not self.is_empty():
if start is None:
p = self.first()
else:
p = self.find_position(start)
if p.key() < start:
p = self.after(p)
while p is not None and (stop is None or p.key() < stop):
yield (p.key(), p.value())
p = self.after(p)

用Python实现数据结构之二叉搜索树的更多相关文章

  1. 【算法与数据结构】二叉搜索树的Java实现

    为了更加深入了解二叉搜索树,博主自己用Java写了个二叉搜索树,有兴趣的同学可以一起探讨探讨. 首先,二叉搜索树是啥?它有什么用呢? 二叉搜索树, 也称二叉排序树,它的每个节点的数据结构为1个父节点指 ...

  2. 数据结构之二叉搜索树、AVL自平衡树

    前言 最近在帮公司校招~~ 所以来整理一些数据结构方面的知识,这些知识呢,光看一遍理解还是很浅的,看过跟动手做过一遍的同学还是很容易分辨的哟~ 一直觉得数据结构跟算法,就好比金庸小说里的<九阳神 ...

  3. hdu 3791:二叉搜索树(数据结构,二叉搜索树 BST)

    二叉搜索树 Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submiss ...

  4. 数据结构之二叉搜索树(BST)--JavaScript实现

    原理: 叉排序树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉排序树的存储结构.中序遍历二叉排序树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程 ...

  5. 自己动手实现java数据结构(六)二叉搜索树

    1.二叉搜索树介绍 前面我们已经介绍过了向量和链表.有序向量可以以二分查找的方式高效的查找特定元素,而缺点是插入删除的效率较低(需要整体移动内部元素):链表的优点在于插入,删除元素时效率较高,但由于不 ...

  6. 数据结构-二叉搜索树的js实现

    一.树的相关概念 1.基本概念 子树 一个子树由一个节点和它的后代构成. 节点的度 节点所拥有的子树的个数. 树的度 树中各节点度的最大值 节点的深度 节点的深度等于祖先节点的数量 树的高度 树的高度 ...

  7. 二叉搜索树 - C++ 实现

    二叉搜索树 - C++ 实现 概述 Overview 二叉查找树(英语:Binary Search Tree, 后文中简称 BST), 也称为二叉搜索树.有序二叉树(ordered binary tr ...

  8. 数据结构-二叉搜索树和二叉树排序算法(python实现)

    今天我们要介绍的是一种特殊的二叉树--二叉搜索树,同时我们也会讲到一种排序算法--二叉树排序算法.这两者之间有什么联系呢,我们一起来看一下吧. 开始之前呢,我们先来介绍一下如何创建一颗二叉搜索树. 假 ...

  9. 【数据结构与算法Python版学习笔记】树——平衡二叉搜索树(AVL树)

    定义 能够在key插入时一直保持平衡的二叉查找树: AVL树 利用AVL树实现ADT Map, 基本上与BST的实现相同,不同之处仅在于二叉树的生成与维护过程 平衡因子 AVL树的实现中, 需要对每个 ...

随机推荐

  1. [每天解决一问题系列 - 0009] File System Redirector

    问题描述: 在64位操作系统下,知道Wow64是干什么的,但一直不知道是怎么工作的 相关解释: https://msdn.microsoft.com/en-us/library/windows/des ...

  2. 批量去除文件的BOM头

    <?php class KillBom{ //定义扩展名 public static $m_ext = ['txt','php','js','css']; /** * 传入一个任意文件,自动区分 ...

  3. DDD实战进阶第一波(三):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架二)

    了解了DDD的好处与基本的核心组件后,我们先不急着进入支持DDD思想的轻量级框架开发,也不急于直销系统需求分析和具体代码实现,我们还少一块, 那就是经典DDD的架构,只有了解了经典DDD的架构,你才能 ...

  4. LearnOpenGL学习笔记(二)——着色器简单理解

    着色器在OpenGL中发挥着重要作用,它就像一个画笔,将输入的数据流,转为数学坐标,再将三维坐标变成二维坐标(针对我们现在用的二维显示器,全息显示器肯是三维的),再把二维坐标实际的像素点位置(这里面肯 ...

  5. 进程间通信IPC-管道

    管道是UNIX系统IPC的最古老的形式,所有的UNIX系统都提供此通讯机制.管道有以下两种局限性: 1, 历史上,它们是半双工的(即数据只能在一个方向上流动).现在某些系统提供了全双工管道,但是为了最 ...

  6. fastdfs集群版搭建(一)- storage集群搭建与统一入口访问

    前言 接着上篇博客:详细的最新版fastdfs单机版搭建,今天来讲讲fastdfs的集群搭建,限于篇幅,今天先搭建stoarge集群,并实现统一的http访问方式: 没看我上篇博客的小伙伴,最好先去瞅 ...

  7. cvte前端笔试后的js原型总结

    最近实习生招聘已经开始了,昨天晚上也终于迎来了第一场笔试,笔试的公司是cvte,笔试题总共27题,25道不定项还有2道编程题,虽然出的都是前端题,但是因为之前没有好好准备,还是很多做的不是很好o(╥﹏ ...

  8. XtraBackup的备份原理与应用示例

    一.XtraBackup简介与安装 XtraBackup是一款免费的在线开源数据库备份解决方案,适用于所有版本的MySQL和MariaDB.XtraBackup支持对InnoDB热备,是一款物理备份工 ...

  9. MVC EF 执行SQL语句(转载)

    MVC EF 执行SQL语句 最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精 闲着没事,看了一篇关于LI ...

  10. Bootstrap学习记录-2.container和table

    1. Container Bootstrap中容器类提供了2个类标识:container.container-fluid. 两者的区别在于: container:容器不止有15px的padding,还 ...