本节主要介绍了树表的查找;二叉排序树的定义、性质;判定树与二叉排序树的关系、二叉排序树如何生成;二叉排序树的查找、插入、删除等操作以及相关代码的图解。
#二叉排序树的相关操作
#二叉排序树其实就是小的在左,大的在右
class BSTNode:#创立二叉排序树的结点类
def __init__(self,k,d=None,l=None,r=None):
self.key=k#key就是结点,用来标记结点当中的数据
self.data=d#这个data域用来存放除关键字外的其他的数据项。一般不用
self.lchild=l#二叉排序树仍然是链式存储结构,所以依然有左孩子指针域和右孩子指针域
self.rchild=r
class BSTClass:#二叉排序树的建树
def __init__(self):
self.r=None#用r来标记二叉排序树的根结点
self.f=None#这个f的位置用来实现删除结点的操作,用来标记要删除结点的双亲结点,链式存储结构的删除操作只要把指针域的指向改一下就行
def InsertBST(self,k,d):#插入一个(k,d)的结点
self.r=self._InsertBST(self,r,k,d)#以根节点为双亲结点,插入一个k,d的结点,k是关键字,d是存放其他数据元素
#建立二叉排序树其实就是一个个找准插入的双亲结点然后把要插入的结点插入进去
def _InsertBST(self,p,k,d):#以p为双亲结点插入一个数据项为(k,d)的结点,k为关键字,d为除关键字的其他数据项
if p==None:#如果双亲结点为空,说明直接建立一个根节点即可
p=BSTNode(k,d)
elif k<p.key:#如果要插入结点的值比双亲结点的关键字的值小,那么就进入左子树,以双亲结点的左孩子结点为根节点再次进行判断
p.lchild=self._InsertBST(p.lchild,k,d)#更新根节点
elif k>p.key:#如果要插入结点的值比双亲结点的关键字的值大,那么就进入右子树,以双亲结点的右孩子结点为根节点再次进行判断
p.rchild=self._InsertBST(p.rchild,k,d)#更新根节点
else:#如果插入结点的值等于这个结点的值,那么就不要再进行操作,直接修改可能修改的data就行
p.data=d
return p
def CreateBST(self,a):#创建一个二叉排序树,其实就是将一个序列的结点一个一个的插入,其中a是一个由(k,d)组成的列表,其中k是关键字的值,d是除了关键字之外储存其他 元素的值
self.r=BSTNode(a[0][0],a[0][1])#先建立一个根节点
for i in range(1,len(a)):
self.InsertBST(self.r,a[i][0],a[i][1])
def SearchBST(self,k):#查找二叉排序树中关键字为k的结点,找到的话返回这个结点
return self._SearchBST(self.r,k)
def _SearchBST(self,p,k):#p是访问的当前结点的指针
if p==None:return None
if p.key==k:return p
if k<p.key:#要找的结点小于当前的结点的关键字,根据二叉排序树的性质,只能从其左子树开始找
return self._SearchBST(p.lchild,k)
else:#要找的结点大于当前的结点的关键字,根据二叉排序树的性质,只能从其右子树开始找
return self._SearchBST(p.rchild,k)
#删除二叉排序树的关键字为k的结点
def DeleteBST(self,k):
self.f=None#f是在删除结点所要用的当前结点双亲结点的指针
return self._DeleteBST(self.r,k,-1)#r是二叉排序树的根节点
#p是当前结点的指针,flag用于辅助删除时的操作,flag用来标记当前结点是其双亲结点的左孩子还是右孩子,方便其修改左孩子指针域或者右孩子指针域从而实现相应的删除操作
#主要分为两步:找到相应的结点在哪,分类讨论实现删除这个结点的操作
#这个是找到这个结点在哪
def _DeleteBST(self,p,k,flag):
if p==None:#删除的结点不存在
return False
if p.key==k:#如果找到了这个结点在哪,就要执行删除这个结点的操作
return self.DeleteNode(p,self.f,flag)
if k<p.key:#要找的结点小于当前的结点的关键字,根据二叉排序树的性质,只能从其左子树开始找
self.f=p#并且在此时移入标记双亲结点的指针f
return self._DeleteBST(p.lchild,k,0)#向左继续执行这个操作,根节点变成其左孩子结点,并且标记是往左下走的,flag置为0
else:
self.f=p#并且在此时移入标记双亲结点的指针f
return self._DeleteBST(p.rchild,k,1)#向右继续执行这个操作,根节点变成其右孩子结点,并且标记是往右下走的,flag置为1
def DeleteNode(self,p,f,flag):#删除这个结点
#删除二叉排序树的结点,要在这个删除结点的子树中找到邻接的结点进行连接,一般我们使用这个结点的左子树中最大的结点,也就是左子树中的最右下角的结点
#这个结点的特征是没有右孩子结点,可能有左孩子结点
#flag是标记要删除的结点的双亲结点的情况,标记要删除的结点是双亲结点的左孩子还是右孩子
#flag是形容要删除结点上边的关系,p.rchild==None是判断要删除结点下面结点的关系
#并且这个算法巧妙在把只有预估结点的情况拿了出来,之后可以服务于删除有两个孩子结点的结点
if p.rchild==None:#删除的结点没有右孩子,只有左孩子
if flag==-1:#flag==-1说明要删除的结点没有双亲,其是根节点,删除的结点是根节点,只要将根节点换成左子树中最大的就行,换的时候要删除换的结点,而被删除的结点只留一个壳换一个新值就行
self.r=p.lchild#从形态来说,最大的结点就是其左孩子结点
elif flag==0:#说明要删除结点是其双亲结点的左孩子结点,删除的时候最大的是左孩子结点,删除的时候要用删除的结点的双亲结点的左孩子的指针进行删除
self.f.lchild=p.lchild
else:#flag=1说明要删除结点是其双亲结点的右孩子结点,删除的时候最大的是左孩子结点,删除的时候要用删除的结点的双亲结点的右孩子的指针进行删除
self.f.rchild=p.lchild
#这段代码与上边的代码类似,只是要删除的结点没有左孩子,要在右子树之中找一个最小的进行替换,即最左下角的进行替换,这个结点的特点是没有左孩子,可能有右孩子
#因为要删除的结点只有右孩子,最小的就是要删除结点的右孩子
elif p.lchild==None:#删除的结点没有左孩子,只有右孩子
if flag==-1:
self.r=p.rchild
elif flag==0:
self.f.lchild=p.rchild
else:
self.f.rchild=p.rchild
else:#删除的结点有左右孩子
#主要分两个部分:找到可以填在要删除结点的结点,为它的移动做好准备;用找到的结点的值替代要删除结点的值
#为移动做准备的工作:因为要移动的结点始终是子树中最大或者最小的结点,所以删除这个结点只要用孩子结点顶替就行
f1=p#f1用来标记移动结点的双亲结点
#以下是找左子树中的最大结点,往右下找
#画个图一下子就出来了
q=p.lchild
if q.rchild==None:#如果q没有右子树,说明q就是最大的
#将值替代
p.key=q.key
p.data=q.data
#把q删除
p.lchild=q.lchild
else:#如果q有右子树,说明要去找最大的那个
while q.rchild!=None:#一直往右下找,才能找到最大的那个,此时q与f同时往右下移动,就是不断的找其右子树,直到右子树为None
f1=q
q=q.rchild
#找到之后把值替代
p.key=q.key
p.data=q.data
#删除结点q
f1.rchild=q.lchild
return True