Python实现二叉搜索树的删除功能

二叉搜索树(二叉查找树,Binary Search Tree)又称为排序二叉树、有序二叉树。

二叉搜索树的实现可以参考:https://blog.csdn.net/weixin_43790276/article/details/105753543

本文使用 Python 实现二叉搜索树的删除功能,在此之前必须先知道二叉搜索树的特性:

1. 如果二叉树的左子树不为空,则左子树上所有节点的值均小于它的根节点的值。

2. 如果二叉树的右子树不为空,则右子树上所有节点的值均大于它的根节点的值。

3. 如果独立地看,左子树、右子树也分别为二叉搜素树。

一、准备二叉搜索树类

在实现二叉搜索树的删除功能前,先实现一个二叉搜索树的类 SearchBinaryTree 。

# coding=utf-8
class Node(object):"""节点类"""def __init__(self, data, left_child=None, right_child=None):self.data = dataself.parent = Noneself.left_child = left_childself.right_child = right_childclass SearchBinaryTree(object):"""二叉树类"""def __init__(self):self.__root = Noneself.prefix_branch = '├'self.prefix_trunk = '|'self.prefix_leaf = '└'self.prefix_empty = ''self.prefix_left = '─L─'self.prefix_right = '─R─'def is_empty(self):return not self.__root@propertydef root(self):return self.__root@root.setterdef root(self, value):self.__root = value if isinstance(value, Node) else Node(value)def show_tree(self):if self.is_empty():print('空二叉树')returnprint('-' * 20)print(self.__root.data)self.__print_tree(self.__root)print('-' * 20)def insert(self, root, value):"""二叉搜索树插入节点-递归"""node = value if isinstance(value, Node) else Node(value)if self.is_empty():self.root = nodereturnif root is None:root = nodeelif node.data < root.data:root.left_child = self.insert(root.left_child, value)root.left_child.parent = rootelif node.data > root.data:root.right_child = self.insert(root.right_child, value)root.right_child.parent = rootreturn rootdef __print_tree(self, node, prefix=None):if prefix is None:prefix, prefix_left_child = '', ''else:prefix = prefix.replace(self.prefix_branch, self.prefix_trunk)prefix = prefix.replace(self.prefix_leaf, self.prefix_empty)prefix_left_child = prefix.replace(self.prefix_leaf, self.prefix_empty)if self.has_child(node):if node.right_child is not None:print(prefix + self.prefix_branch + self.prefix_right + str(node.right_child.data))if self.has_child(node.right_child):self.__print_tree(node.right_child, prefix + self.prefix_branch + ' ')else:print(prefix + self.prefix_branch + self.prefix_right)if node.left_child is not None:print(prefix + self.prefix_leaf + self.prefix_left + str(node.left_child.data))if self.has_child(node.left_child):prefix_left_child += '  'self.__print_tree(node.left_child, self.prefix_leaf + prefix_left_child)else:print(prefix + self.prefix_leaf + self.prefix_left)def has_child(self, node):return node.left_child is not None or node.right_child is not Nonedef __str__(self):return str(self.__class__)

上面的代码实现了一个节点类 Node,实现了二叉搜索树的类 SearchBinaryTree。在 SearchBinaryTree 中,实现了判断二叉搜索树是否为空的 is_empty() 方法、一对供实例对象调用的 root() 方法、按树形结构打印二叉搜索树的 show_tree() 方法和添加数据到二叉搜索树中的 insert(root, value)方法。

现在先在树中添加一些节点,供后面的删除方法使用。

if __name__ == '__main__':tree = SearchBinaryTree()data = [50, 77, 55, 29, 10, 30, 66, 80, 51, 18, 90, 78, 79]for i in data:tree.insert(tree.root, i)tree.show_tree()

运行结果:

--------------------
50
├─R─77
| ├─R─80
| | ├─R─90
| | └─L─78
| |   ├─R─79
| |   └─L─
| └─L─55
|   ├─R─66
|   └─L─51
└─L─29├─R─30└─L─10├─R─18└─L─
--------------------

添加数据后的二叉搜索树如下图:

添加完成节点后,继续准备删除节点时需要使用到的方法。返回二叉搜索树的高度的方法 height(root),用于判断被删除的节点在树的第几层。在二叉搜索树中查找节点的方法 search(root, data),要从二叉搜索树中删除节点,首先要保证节点是属于二叉搜索树的,所以先搜索节点是否在二叉搜索树中。返回二叉搜索树中值最大的节点的方法 get_max(root) 和返回值最小的节点的方法 get_min(root) ,用于寻找被删除节点的前继节点或后继节点。

    def height(self, root):"""二叉树的深度"""if root.data is None:return 0if root.left_child is None and root.right_child is None:return 1if root.left_child is not None and root.right_child is None:return 1 + self.height(root.left_child)if root.left_child is None and root.right_child is not None:return 1 + self.height(root.right_child)if root.left_child is not None and root.right_child is not None:return 1 + max(self.height(root.left_child), self.height(root.right_child))def search(self, root, data):"""二叉搜索树的查询操作"""if root is None:returnif root.data == data:return rootelif data < root.data:return self.search(root.left_child, data)elif data > root.data:return self.search(root.right_child, data)def get_max(self, root):"""查找二叉搜索树值最大的节点"""if self.is_empty():returnreturn self.get_max(root.right_child) if root.right_child else rootdef get_min(self, root):"""查找二叉搜索树值最小的节点"""if self.is_empty():returnreturn self.get_min(root.left_child) if root.left_child else root

二、二叉搜索树删除节点

如果被删除的节点是叶节点,比较好办,找到这个节点,然后删除。如果被删除的节点不是叶节点,节点有左子树或右子树,或两者都有,删除节点后,左子树和右子树会跟着节点一起从树中“断”掉,那就不仅仅是删除节点了,而是把子树给全部删除了。所以,删除非叶节点时,必须从子树中选择一个节点来填补被删除的节点位置,避免树的断裂,也避免“牵连”到其他的节点,还要保证删除节点后的二叉树依然是一棵二叉搜索树,满足二叉搜索树的特性。

由简到繁,根据被删除节点是否有左右子树,二叉搜索树的删除可以分为三种情况,被删除节点为叶节点、被删除节点只有一棵子树和被删除节点有两棵子树。

1. 被删除节点为叶节点,即被删除节点没有左子树和右子树。删除叶节点后,不会破坏整棵树的结构,只要找到此节点,然后将节点从二叉树中“断开”(父节点的指针指向空)即可。在上面添加数据后的二叉搜索树中,随机选一个叶节点,如 66。

    def _delete_no_child(self, node):"""删除叶节点"""if node == self.root:self.root = Nonereturnif node == node.parent.left_child:node.parent.left_child = Noneelse:node.parent.right_child = Nonenode.parent = None

_delete_no_child(node): 删除叶节点的方法。这个方法只属于删除的部分功能,所以在前面加一个下划线,表示等删除功能完全实现后,不会再直接使用。

    node = tree.search(tree.root, 66)tree._delete_no_child(node)tree.show_tree()

运行结果:

--------------------
50
├─R─77
| ├─R─80
| | ├─R─90
| | └─L─78
| |   ├─R─79
| |   └─L─
| └─L─55
|   ├─R─
|   └─L─51
└─L─29├─R─30└─L─10├─R─18└─L─
--------------------

删除节点 66 后的二叉搜索树结构如下图。

2. 被删除节点只有一棵子树,即被删除节点只有左子树或只有右子树。删除节点后,为了保证节点的子树不被“牵连”,要将节点的子树进行“补位”。如删除下图中的节点 10,被删除节点是其父节点的左子节点,被删除节点有右子树,则删除节点后,直接将被删除节点的右子树补位成为其父节点的左子树。

    def _delete_one_child(self, node):"""删除有一个子节点的节点"""if node.left_child:if node.parent and node.parent.left_child == node:node.left_child.parent = node.parentnode.parent.left_child = node.left_childelif node.parent and node.parent.right_child == node:node.left_child.parent = node.parentnode.parent.right_child = node.left_childelse:self.root = node.left_childnode.left_child.parent = Nonenode.left_child = Noneelse:if node.parent and node.parent.left_child == node:node.right_child.parent = node.parentnode.parent.left_child = node.right_childelif node.parent and node.parent.right_child == node:node.right_child.parent = node.parentnode.parent.right_child = node.right_childelse:self.root = node.right_childnode.right_child.parent = Nonenode.right_child = Nonenode.left_child = Nonenode.parent = None

_delete_one_child(node): 删除只有一棵子树的节点。同理,这个方法也只属于删除的部分功能,所以在前面加一个下划线,表示删除功能完全实现后,不会直接使用。

    node = tree.search(tree.root, 10)tree._delete_one_child(node)tree.show_tree()

运行结果:

--------------------
50
├─R─77
| ├─R─80
| | ├─R─90
| | └─L─78
| |   ├─R─79
| |   └─L─
| └─L─55
|   ├─R─66
|   └─L─51
└─L─29├─R─30└─L─18
--------------------

删除节点 10 后的二叉搜索树结构如下图。

3. 被删除节点有两棵子树,即被删除节点同时有左子树和右子树。删除节点后,为了避免两棵子树从树中“断裂”,必须找到一个节点来填补被删除节点的位置。如删除下图中的节点 77,必须找一个节点来补位,否则两棵子树也会被删除。

为了保证删除节点后二叉树仍然是一棵二叉搜索树,补位节点有两种选择方式,选择被删除节点的前继节点或后继节点,前继节点一定存在左子树中,后继节点一定存在右子树中,选择两种方式都可以,本文选择后继节点。

那什么是后继节点和前继节点?如果将二叉搜索树中的节点按存储的数值的大小升序排列,排在当前节点的后一个节点就是后继节点,排在当前节点的前一个节点就是前继节点。节点 77 的后继节点是 78。

将节点删除后,找到它的后继节点,将后继节点补位到被删除节点的位置,这样就保证了被删除节点的两棵子树没有断裂。但是还没有完,后继节点补位之后,相当于是将后继节点从它原来的位置删除了。节点 78 补位到 77 的位置后,相当于将节点 78 从原来的位置删除了。

所以还要对后继节点进行处理,后继节点可能是叶节点,可以用情况1,后继节点可能有右子树,可以用情况2,如节点 78,后继节点不可能有左子树,不用考虑。

为什后继节点一定没有左子树?假设后继节点有左子树,根据二叉搜索树的特性,后继节点的值比当前节点大,后继节点一定在当前节点的右子树中,而后继节点的左子树中的节点值要小于后继节点的值,这样,就存在比当前节点大又比后继节点小的节点,与后继节点的定义矛盾,所以假设不成立。

当然这里并不是说二叉搜索树中所有节点的后继节点都没有左子树,而是当一个节点有两棵子树时,它的后继节点不可能有左子树。

到这里,删除节点 77 分为两步,第一步是找到后继节点 78,将节点 78 补位到 77 的位置,第二步是删除节点 78 。

    def delete(self, value):"""二叉搜索树删除节点"""if self.height(self.root) >= 1:node_delete = self.search(self.root, value)if node_delete:self._delete(node_delete)else:raise KeyError("Error, value not in this tree")else:raise KeyError("Error, value not in this tree")def _delete(self, node):"""删除节点"""if not node.left_child and not node.right_child:self._delete_no_child(node)elif node.left_child and not node.right_child or not node.left_child and node.right_child:self._delete_one_child(node)else:rear_node = self.get_min(node.right_child)node.data = rear_node.dataself._delete(rear_node)

_delete(node): 删除节点的方法。该方法中通过 get_min() 方法找到当前节点的后继节点 rear_node ,然后进行补位操作。而删除后继节点的操作,只要递归一次就可以,因为删除后继节点的方法一定属于前面的两种情况之一。

delete(node): 删除节点的通用方法。前面提过,要删除节点,首先节点要属于当前二叉搜索树。所以无法指定节点来删除,只能根据节点中存储的值,先在树中进行搜索,找到了再执行删除操作。

    tree.delete(77)tree.show_tree()

运行结果:

--------------------
50
├─R─78
| ├─R─80
| | ├─R─90
| | └─L─79
| └─L─55
|   ├─R─66
|   └─L─51
└─L─29├─R─30└─L─10├─R─18└─L─
--------------------

删除节点 77 后的二叉搜索树结构如下图。

三、完整代码

# coding=utf-8
class Node(object):"""节点类"""def __init__(self, data, left_child=None, right_child=None):self.data = dataself.parent = Noneself.left_child = left_childself.right_child = right_childclass SearchBinaryTree(object):"""二叉树类"""def __init__(self):self.__root = Noneself.prefix_branch = '├'self.prefix_trunk = '|'self.prefix_leaf = '└'self.prefix_empty = ''self.prefix_left = '─L─'self.prefix_right = '─R─'def is_empty(self):return not self.__root@propertydef root(self):return self.__root@root.setterdef root(self, value):self.__root = value if isinstance(value, Node) else Node(value)def show_tree(self):if self.is_empty():print('空二叉树')returnprint('-' * 20)print(self.__root.data)self.__print_tree(self.__root)print('-' * 20)def insert(self, root, value):"""二叉搜索树插入节点-递归"""node = value if isinstance(value, Node) else Node(value)if self.is_empty():self.root = nodereturnif root is None:root = nodeelif node.data < root.data:root.left_child = self.insert(root.left_child, value)root.left_child.parent = rootelif node.data > root.data:root.right_child = self.insert(root.right_child, value)root.right_child.parent = rootreturn rootdef delete(self, value):"""二叉搜索树删除节点"""if self.height(self.root) >= 1:node_delete = self.search(self.root, value)if node_delete:self._delete(node_delete)else:raise KeyError("Error, value not in this tree")else:raise KeyError("Error, value not in this tree")def _delete(self, node):"""删除节点"""if not node.left_child and not node.right_child:self._delete_no_child(node)elif node.left_child and not node.right_child or not node.left_child and node.right_child:self._delete_one_child(node)else:rear_node = self.get_min(node.right_child)node.data = rear_node.dataself._delete(rear_node)def _delete_no_child(self, node):"""删除叶节点"""if node == self.root:self.root = Nonereturnif node == node.parent.left_child:node.parent.left_child = Noneelse:node.parent.right_child = Nonenode.parent = Nonedef _delete_one_child(self, node):"""删除有一个子节点的节点"""if node.left_child:if node.parent and node.parent.left_child == node:node.left_child.parent = node.parentnode.parent.left_child = node.left_childelif node.parent and node.parent.right_child == node:node.left_child.parent = node.parentnode.parent.right_child = node.left_childelse:self.root = node.left_childnode.left_child.parent = Nonenode.left_child = Noneelse:if node.parent and node.parent.left_child == node:node.right_child.parent = node.parentnode.parent.left_child = node.right_childelif node.parent and node.parent.right_child == node:node.right_child.parent = node.parentnode.parent.right_child = node.right_childelse:self.root = node.right_childnode.right_child.parent = Nonenode.right_child = Nonenode.left_child = Nonenode.parent = Nonedef height(self, root):"""二叉树的深度"""if root.data is None:return 0if root.left_child is None and root.right_child is None:return 1if root.left_child is not None and root.right_child is None:return 1 + self.height(root.left_child)if root.left_child is None and root.right_child is not None:return 1 + self.height(root.right_child)if root.left_child is not None and root.right_child is not None:return 1 + max(self.height(root.left_child), self.height(root.right_child))def search(self, root, data):"""二叉搜索树的查询操作"""if root is None:returnif root.data == data:return rootelif data < root.data:return self.search(root.left_child, data)elif data > root.data:return self.search(root.right_child, data)def get_max(self, root):"""查找二叉搜索树值最大的节点"""if self.is_empty():returnreturn self.get_max(root.right_child) if root.right_child else rootdef get_min(self, root):"""查找二叉搜索树值最小的节点"""if self.is_empty():returnreturn self.get_min(root.left_child) if root.left_child else rootdef __print_tree(self, node, prefix=None):if prefix is None:prefix, prefix_left_child = '', ''else:prefix = prefix.replace(self.prefix_branch, self.prefix_trunk)prefix = prefix.replace(self.prefix_leaf, self.prefix_empty)prefix_left_child = prefix.replace(self.prefix_leaf, self.prefix_empty)if self.has_child(node):if node.right_child is not None:print(prefix + self.prefix_branch + self.prefix_right + str(node.right_child.data))if self.has_child(node.right_child):self.__print_tree(node.right_child, prefix + self.prefix_branch + ' ')else:print(prefix + self.prefix_branch + self.prefix_right)if node.left_child is not None:print(prefix + self.prefix_leaf + self.prefix_left + str(node.left_child.data))if self.has_child(node.left_child):prefix_left_child += '  'self.__print_tree(node.left_child, self.prefix_leaf + prefix_left_child)else:print(prefix + self.prefix_leaf + self.prefix_left)def has_child(self, node):return node.left_child is not None or node.right_child is not Nonedef __str__(self):return str(self.__class__)if __name__ == '__main__':tree = SearchBinaryTree()data = [50, 77, 55, 29, 10, 30, 66, 80, 51, 18, 90, 78, 79]for i in data:tree.insert(tree.root, i)# tree.show_tree()# node = tree.search(tree.root, 66)# tree._delete_no_child(node)# tree.show_tree()# node = tree.search(tree.root, 10)# tree._delete_one_child(node)# tree.show_tree()tree.delete(80)tree.show_tree()

Python实现二叉搜索树的删除功能相关推荐

  1. Python实现二叉搜索树

    Python实现二叉搜索树 二叉搜索树(二叉查找树,Binary Search Tree)是一种特殊的二叉树,又称为排序二叉树.有序二叉树. 二叉搜索树具有如下特性: 1. 如果二叉树的左子树不为空, ...

  2. 08_Python算法+数据结构笔记-二叉搜索树查询/删除-AVL树旋转/插入/应用-贪心算法

    b站视频:路飞IT学城 清华计算机博士带你学习Python算法+数据结构_哔哩哔哩_bilibili #71 二叉搜索树:查询 import randomclass BiTreeNode:def __ ...

  3. 数据结构 二叉搜索树的删除

    文章目录 概述 待删除的结点没有子树 待删除的结点仅有一颗子树 待删除的结点有两颗子树 C代码实现 概述 这是一篇短文,专门考究一下二叉搜索树的删除. 二叉搜索树的建立非常简单,如果不熟悉的见此文 树 ...

  4. 二叉搜索树的删除操作可以交换吗_JavaScript数据结构 — 二叉搜索树(BST)ES6实现...

    1. 概述 最基本的数据结构是向量和链表,为了将二者的优势结合起来,我们引入了二叉树,可以认为二叉树是列表在维度上的拓展.而今天要介绍的二叉搜索树(BST)则是在形式上借鉴了二叉树,同时也巧妙借鉴了有 ...

  5. 二叉搜索树的删除操作可以交换吗_一文看懂数据结构中的树

    通常在开始学编程的时候,你会接触一些常用数据结构.到最后一般会学到哈希表.对于修读计算机科学学位的朋友,你通常要上专门的数据结构课,从了解有关链表.队列和栈的各种知识.这些统称为线性数据结构,因为依逻 ...

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

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

  7. 7-2 二叉搜索树的删除操作

    给出一棵二叉搜索树(没有相同元素), 请输出其删除部分元素之后的层序遍历序列. 删除结点的策略如下: 如果一个结点是叶子结点,则直接删除: 如果一个结点的左子树不为空, 则将该结点的值设置为其左子树上 ...

  8. python实现二叉搜索树_python实现二叉查找树

    这次用完成的是二叉树,是一种简单的树型结构.同样使用python实现 多的不说了,上代码吧. # -*- coding: cp936 -*- #---------------------------- ...

  9. 算法:二叉搜索树BST - 删除节点 详解(Java实现)

    删除节点 删除节点存在 3 种情况,几乎所有类似博客都提到了这点.这 3 种情况分别如下: 没有左右子节点,可以直接删除 存在左节点或者右节点,删除后需要对子节点移动 同时存在左右子节点,不能简单的删 ...

最新文章

  1. linux系统用户属组,关于 Linux系统用户、组和权限管理
  2. 分享:一个简单的线程池的实现
  3. 用java解决国王的金币问题_国王赏赐金币问题(减治法)
  4. PMCAFF | 产品经理挑战赛,等你来战
  5. PHP学习记录之会话处理(二)
  6. JS中的异步任务有哪些
  7. Python 程序和 C 程序的整合
  8. java接收前台tex格式t数据_java 下载文件时,设置response.setContentType 根据文件类型...
  9. double float区别 java,float和double有什么区别?
  10. 【python工具篇】pip和pypi
  11. 计算机教室的教师端,极域电子教室教师端使用.doc
  12. Debian10安装Chromium浏览器
  13. 万年历(hao123)代码
  14. 【上电即上华为云】华为云smart智联Cat.1+PLC无线网关_3121N-IED_MC615-CN-L610-CN
  15. FPGA集成开发环境
  16. poi实现多线程大数据导出
  17. 实战Nagios NSCA方式监控Linux系统资源使用情况 -- Nagios配置篇 -- 被监控端
  18. 普通pc机的轻连接到底能建多少?
  19. 深度学习系列 -- 第二门课 改善深层神经网络:超参数调试、正则化以及优化(一):深度学习的实践层面(Practical aspects of Deep Learning)
  20. SpringBoot整合微信支付开发在线教育视频网站(完整版)

热门文章

  1. python 读shell
  2. 201521123040《Java程序设计》第10周学习总结
  3. webdynpro 组件重用 传值问题
  4. Java Web编程技术
  5. Ubuntu下的文件安全删除工具
  6. 使用ACDsee在一张纸上打印多张图片
  7. MVC3.0 将网站设为首页和加为收藏的实现(IE/Firefox)
  8. vs的form标签引起css走样问题
  9. Linux远程桌面服务VNC/XRDP/Xdmcp/SSH+X11转发及其在树莓派上的使用
  10. JSF Spring Hibernate集成示例教程