二叉排序树

  • 引言
  • 1、定义
  • 2、性质
  • 3、操作
    • 3.1 查找
    • 3.2 插入
    • 3.3 生成
    • 3.4 删除

引言

  • 如何更加高效的完成对数据的查询和添加操作,例如↓↓↓

    给你一个数列 (7, 3, 10, 12, 5, 1, 9),要求能够高效的完成对数据的查询和添加。

  • 已有解决方案的优缺点

  • 树结构解决方案:
    二叉排序树

1、定义

\quad \quad二叉排序树又称二叉搜索树、二叉查找树。

\quad \quad二叉排序树或是空树,或是满足以下性质的二叉树:

(1)若其左子树非空,则左子树上所有结点的值小于根结点的值;

(2)若其右子树非空,则右子树上所有结点的值均大于等于根结点的值;

(3)其左右子树本身又各是一棵二叉排序树

\quad \quad总结起来就是根据结点的值有:左子树<根结点<右子树

\quad \quad如下图就是一棵二叉排序树:

\quad \quad它的中序遍历:8、10、11、12、13、15、16、17、18、19、22、25,刚好是排好序的。

2、性质

\quad \quad中序遍历非空的二叉排序树所得到的数据元素序列是一个按关键字排列的递增有序序列。

3、操作

3.1 查找

【算法思想】——递归

  • 若二叉排序树为空,则查找失败,返回空指针
  • 若二叉排序树非空,将给定值与根结点值比较:
    • 若查找的关键字等于根结点,则查找成功,返回根结点地址

    • 否则

      • 若小于根结点,则进一步查其左子树
      • 若大于根结点,则进一步查其右子树

【代码实现】

def BST_serach(root, value):if not root: # 遇到空节点,未找到return Falseif root.value == value: # 找到return Trueelif value < root.value: # 若值小于根节点值,继续从左子树中查找return BST_serach(root.left, value)else: # 否则,该值大于根节点的值,从右子树中查找return BST_serach(root.right, value)

\quad \quad二叉排序树上查找某关键字等于给定值的结点过程,其实就是走了一条从根到该结点的路径:

  • 比较的关键次数=此结点所在曾次数
  • 最多的比较次数=树的深度

\quad \quad含有n个结点的二叉排序树的平均查找长度ASL和树的形态有关


如何提高形态不均衡的二叉排序树的查找效率?
平衡化处理,尽量让二叉树的形状均衡,转换成平衡二叉树。

3.2 插入

\quad \quad从根结点开始逐个与关键字进行比较,遇键值较大者就向左,遇键值较小者就向右,一直到尾端,记为插入点。
【算法思想】—递归

  • 若二叉排序树为空,则插入结点作为根结点插入到空树中;

  • 否则,继续在左、右树上查找

    • 树中已有,不再插入

    • 树中没有

      • 查找直至某个叶子节点的左子树或右子树为空为止,则插入结点应为该叶子结点的左孩子或右孩子。

【代码实现】

def BST_insert(root, value):if not root: # 遇到空节点,即为插入位置root = Node(value)returnif root.value == value: # 若该值已存在于二叉树中,插入失败print("Existed!!!")returnelif value < root.value: # 若值小于根节点值,继续从左子树中查找插入位置if not root.left: # 如果根结点没有左子树,则插入到左子树中root.left = Node(value)returnBST_insert(root.left, value)# 注:以上四句不可用 return self.BST_insert(root.right, value)代替,# 会出现节点无法插入的问题else: # 否则,该值大于根节点的值,从右子树中查找插入位置if not root.right:root.right = Node(value)returnBST_insert(root.right, value)

\quad \quad插入的元素一定在叶结点上。

3.3 生成

\quad \quad从空树出发,经过一系列的查找、插入操作之后,可生成一棵二叉排序树。

例:设查找的关键字序列为{45,24,53,45,24,12,37,93}


\quad \quad容易看出,每次插入新的结点都是二叉排序树上的叶子结点,则在插入时,不必移动其他结点,只需改动某个结点的指针,由空变为非空即可。结点的插入操作与二叉排序树的定义紧密相关,即左<根<右,新插入一个关键字时,从根结点开始比较,直到找到合适的插入位置为止。还有一种情况就是一个序列中可能有两个相同的关键字,对于这种情况,向树中插入关键字时遇到相同关键字时,什么都不做,不进行重复插入操作。

【代码实现】
方法一:连续使用插入的方法生成二叉排序树

#调用上面的插入函数创建二叉排序树
def createTree(NodeList):"""创建二叉排序树, NodeList为待插入数据的列表"""T = Nonefor i in range(len(NodeList)):T = BST_insert(T, NodeList[i])return T

方法二:使用添加节点的方法创建二叉排序树

# 添加结点
def add(self, val):  node = TreeNode(val)if self.root is None:self.root = nodereturnqueue = [self.root]while queue:temp_node = queue.pop(0)# 判断传入结点的值和当前子树结点的值关系if node.val < temp_node.val:if temp_node.left is None:temp_node.left = nodereturnelse:queue.append(temp_node.left)if node.val >= temp_node.val:if temp_node.right is None:temp_node.right = nodereturnelse:queue.append(temp_node.right)

\quad \quad一个无序序列可通过构造二叉排序树而变成一个有序序列。构造树的过程就是对无序序列进行排序的过程。

注意:关键字的输入顺序不同,生成的二叉排序树形态不同。

3.4 删除

\quad \quad从二叉排序树中删除一个结点,不能把以该结点为根的子树都删去,只能删除该结点,并且还应保证删除后所得的二叉树仍然满足二叉排序树的性质不变。

\quad \quad由于中序遍历二叉排序树可以得到一个递增有序的序列。那么,在二叉排序树中删去一个结点相当于删去有序序列中的一个结点。

  • 将因删除结点而断开的二叉链表重新连接起来;

  • 防止重新链接后树的高度增加。

删除方法考虑以下几个情况:

1、 删除节点为叶子节点
\quad \quad删除的节点没有左子树也没有右子树,也就是删除的节点为叶子节点。有两种情况:
 1. 该叶子节点为二叉排序树的根结点,也就是二叉排序树中只有一个结点,只需要将root的指针置为空即可
 2. 该叶子节点有父节点,将父节点的连接该删除节点的指针置为空即可。

思路
(1) 需求先去找到要删除的结点 targetNode
(2) 找到targetNode 的 父结点 parent
(3) 确定 targetNode 是 parent的左子结点 还是右子结点
(4) 根据前面的情况来对应删除
左子结点 parent.left = null
右子结点 parent.right = null;

2、删除的节点只有左子树或者只有右子树

\quad \quad删除节点后,将父节点指针指向子树即可

  • 若结点T的左子树为空,则用T的右子树替代T,即为删除了T结点

  • 若结点T的右子树为空,则用T的左子树替代T,即为删除了T结点

思路:

(1) 需求先去找到要删除的结点 targetNode
(2) 找到targetNode 的 父结点 parent
(3) 确定targetNode 的子结点是左子结点还是右子结点
(4) targetNode 是 parent 的左子结点还是右子结点
(5) 如果targetNode 有左子结点
5. 1 如果 targetNode 是 parent 的左子结点
parent.left = targetNode.left;
5.2 如果 targetNode 是 parent 的右子结点
parent.right = targetNode.left;
(6) 如果targetNode 有右子结点
6.1 如果 targetNode 是 parent 的左子结点
parent.left = targetNode.right;
6.2 如果 targetNode 是 parent 的右子结点
parent.right = targetNode.right

3、删除的节点同时存在左右子树
\quad \quad如果同时存在左右子树,则可以将二叉排序树进行中序遍历,取将要被删除的节点的前驱或者后继节点替代这个被删除的节点的位置。【前驱是左子树中最大的结点;后继是右子树中最小的结点】

思路
(1) 需求先去找到要删除的结点 targetNode
(2) 找到targetNode 的 父结点 parent
(3) 从targetNode 的右子树找到最小的结点
(4) 用一个临时变量,将 最小结点的值保存 temp = 11
(5) 删除该最小结点
(6) targetNode.value = temp


【代码实现】

class TreeNode(object):def __init__(self, val):self.val = valself.left = Noneself.right = Noneclass BinarySortTree(object):def __init__(self):self.root = None# 添加结点def add(self, val):  node = TreeNode(val)if self.root is None:self.root = nodereturnqueue = [self.root]while queue:temp_node = queue.pop(0)# 判断传入结点的值和当前子树结点的值关系if node.val < temp_node.val:if temp_node.left is None:temp_node.left = nodereturnelse:queue.append(temp_node.left)if node.val >= temp_node.val:if temp_node.right is None:temp_node.right = nodereturnelse:queue.append(temp_node.right)# 中序遍历def in_order(self, node):if node is None:returnself.in_order(node.left)print(node.val, end=" ")self.in_order(node.right)# 删除节点def del_node(self, node, val):''':param node: 传入根结点:param val: 传入要删除结点的值:return:'''if node is None:return# 先去找到要删除的结点target_node = self.search(node, val)if target_node is None:  # 如果没有找到要删除的结点return# 如果发现当前这棵二叉排序树只有一个结点if node.left is None and node.right is None:self.root = None # 根结点直接置空return# 去找到target_node的父结点parent = self.parent(node, val)# 删除的结点是叶子结点if target_node.left is None and target_node.right is None:# 判断target_node 是父结点的左子结点,还是右子结点if parent.left and parent.left.val == val:  # target_node 是左子结点parent.left = Noneelif parent.right and parent.right.val == val:  # target_node 是右子结点parent.right = Noneelif target_node.left and target_node.right:  # 删除有两颗子树的结点min_val = self.del_right_tree_min(target_node.right)target_node.val = min_val# 删除只有一颗子树的结点else:  if target_node.left:  # 如果要删除的结点target_node有左子结点if parent: # 如果target_node有父结点if parent.left.val == val:  # target_node是parent左子结点parent.left = target_node.leftelse:  # parent.right.val == val,即是target_node是parent右子结点parent.right = target_node.leftelse:self.root = target_node.leftelse:  # 如果要删除的结点target_node有右子结点if parent: # 如果target_node有父结点if parent.left.val == val:parent.left = target_node.rightelse:  # parent.right.val == val,即是target_node是parent右子结点parent.right = target_node.rightelse:self.root = target_node.right# 查找结点def search(self, node, val):''':param node: 传入根结点:param val: 传入要查找的值:return: 找到返回该值,没找到返回None'''if node is None:return Noneif node.val == val:return nodeif val < node.val:return self.search(node.left, val)else:return self.search(node.right, val)# 查找结点的父结点def parent(self, node, val):''':param node: 传入根结点:param val: 传入要找的父结点的值:return: 如果找到返回该父结点,如果没有返回None'''if node is None:  # 如果要找的值遍历完二叉树还不存在,由此退出并返回Nonereturn Noneif self.root.val == val:  # 根结点没有父结点return None# 如果当前结点的左子结点或者右子结点存在,并且值就是要要找的,直接返回它的父结点if (node.left and node.left.val == val) or (node.right and node.right.val == val):return nodeelse:# 如果要找的结点值小于父结点且它的左子结点存在,向左递归if val < node.val and node.left:return self.parent(node.left, val)# 如果要找的结点值大于父结点且它的右子结点存在,向左递归elif node.val < val and node.right:return self.parent(node.right, val)def del_right_tree_min(self, node):# 从target_node的右子树出发,查找它的左边最小结点,并返回删除结点的值# 作用1:返回以node为根结点的二叉排序树的最小结点# 作用2:删除 node 为根结点的二叉排序树的最小结点temp_node = node# 循环的查找左结点,直到找到最小值while temp_node.left:temp_node = temp_node.left# 这时 target就指向了最小结点# 调用删除方法,删除最小结点self.del_node(self.root, temp_node.val) # 注意传入的还是跟结点,从根结点开始查找return temp_node.valif __name__ == '__main__':t = BinarySortTree()note_array = [7, 3, 10, 12, 5, 1, 9, 2]for item in note_array:t.add(item)'''# 测试:删除叶子结点t.del_node(t.root, 2)t.del_node(t.root, 5)t.del_node(t.root, 9)t.del_node(t.root, 12)t.in_order(t.root) # 1 3 7 10''''''# 测试:删除只有一颗子树的结点t.del_node(t.root, 1)t.in_order(t.root) # 2 3 5 7 9 10 12 '''# 测试:删除有两颗子树的结点# t.del_node(t.root, 7)# t.in_order(t.root) # 1 2 3 5 9 10 12# t.del_node(t.root, 10)# t.in_order(t.root)  # 1 2 3 5 7 9 12# t.in_order(t.root)# 连续删除任意结点测试:t.del_node(t.root, 2)t.del_node(t.root, 5)t.del_node(t.root, 9)t.del_node(t.root, 12)t.del_node(t.root, 7)# t.del_node(t.root, 3)# t.del_node(t.root, 10)# t.del_node(t.root, 1)t.in_order(t.root)

详解二叉排序树(二叉搜索树、二叉查找树)以及Python实现相关操作相关推荐

  1. 二十分钟深入详解<二叉搜索树>!!!

    目录 前文 一,什么是二叉搜索树? 1.1 二叉搜索树的概念 二, 二叉搜索树的常用操作及其实现 2.1 查找 2.2 插入 2.3 删除 三,二叉搜索树的应用 3.1 K模型 3.2 KV模型 四, ...

  2. 【数据结构笔记11】二叉搜索树,动态查找,删除操作

    本次笔记内容: 4.1.1 二叉搜索树及查找 4.1.2 二叉搜索树的插入 4.1.3 二叉搜索树的删除 文章目录 动态查找 什么是二叉搜索树(BST) 二叉树的操作 二叉搜索树的查找操作 二叉搜索树 ...

  3. 【算法•日更•第九期】树型动态规划详解:二叉苹果树

    ▎前置技能:动态规划&树 树型动态规划一听就知道是在树结构上使用的动态规划,那么不会树结构和动态规划怎么行?戳这里了解动态规划和树. ▎什么是树型动态规划? ☞『定义』 树形动态规划问题可以分 ...

  4. 算法入门篇三 详解桶排序和整理排序知识 堆的相关操作 补充 不完整

    归并排序不使用递归 使用一个变量,使其按照1.2.4.8递增,控制左右两边1个元素.2个元素.4个元素等元素的合并 完全二叉树 完全二叉树 要不全是满的,要不叶子节点出现在最后一层,只要出现了叶子节点 ...

  5. mysql中的tee_详解mysql的tee功能 并利用其记录相关操作

    由于经常对mysql数据库进行大量的更改操作,比如更改字段,添加或删除索引等等,我们把这些操作放到sql语句中,然后登陆mysql,通过source执行该sql文件,为了做好相关记录,方便以后的工作中 ...

  6. 二叉搜索树详解--实现插入和删除

    目录 BST树概念 BST树操作 BST树的查找 BST树的插入 BST树的删除 实现一个自己的BST树 BSTNode类和BSTree类 查找操作; 插入操作: 删除操作: 应用: 二叉搜索树性能分 ...

  7. 数组模拟二叉搜索树(二叉排序树)

    文章目录 1. 二叉搜索树的定义 2. 二叉搜索树经典模板 2.1 插入操作(建树操作) 2.2 删除操作 2.3 查询二叉搜索树中值为 w 的前驱/后继数值 3. 经典例题 1. 二叉搜索树的定义 ...

  8. 将字符串转换为数组_LeetCode 树 108.将有序数组转换为二叉搜索树

    7(108) 将有序数组转换为二叉搜索树 描述 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 ...

  9. 二叉搜索树的删除_LeetCode109.有序链表转换二叉搜索树

    点击上方"蓝字",发现更多精彩. 01PART二叉搜索树二叉查找树又称二叉搜索树或者二叉排序树:它可以是一个空树或者是一个二叉树,既有链表的快速插入与删除的特点,又有数组快速查找的 ...

  10. 算法导论笔记:12二叉搜索树

    1:概念 二叉搜索树也叫二叉排序树,它支持的操作有:SEARCH, MINIMUM, MAXIMUM, PREDECESSOR, SUCCESSOR, INSERT, DELETE.所以,一颗二叉搜索 ...

最新文章

  1. Linux下显示IP地址所在地信息的小工具——nali
  2. 手把手教你做关键词匹配项目(搜索引擎)---- 第九天
  3. java 面试问jdk版本_面试常被问到的 JDK 命令,你知道几个?
  4. Java开源爬虫框架crawler4j
  5. 堆积柱形图显示总数_送你一份堆积柱形图小点心,请收下~
  6. jsp页面判断输入编号已存在mysql中_面试官让我聊聊Mysql基础架构之日志文件与数据文件...
  7. mysql替换字段的部分数据
  8. ASP.NET状态管理详解,让你明明白白
  9. 使用cp命令拷贝目录下指定文件外的其他文件
  10. ios mysql 创建不同的用户表_移动端iOS系统数据库之Realm(二)表的创建增删改查(多表)...
  11. Python基础课程笔记·嵩天
  12. Python+Flask(2)--通过flask paginate解决列表分页问题
  13. 页面获取服务器图片路径问题
  14. 芯片制造良率分析系统YMS
  15. 任务二:移动通信发展史
  16. 湖北大学知行学院考研计算机,湖北大学知行学院考研怎么样
  17. vi 编辑器进入后无法退出
  18. Coda 2.5 发布,Mac 编辑器软件
  19. Encoder编码器、Decoder解码器
  20. 爬取天气数据进行绘制折线图

热门文章

  1. 贵翔阳光房的设计风格
  2. [附源码]Java计算机毕业设计SSM电影票购票系统
  3. 在Windows10右下角显示时间
  4. 排序 归并排序_56
  5. tesseract简单数字英文识别
  6. 一招教你单片机固件快速瘦身
  7. 测试工程师之Python面试题(附答案)
  8. 云服务器好还是高防服务器好
  9. jQuery 第一弹
  10. 【Python】机器学习笔记08-流形学习(Manifold Learning)