二叉查找树BST
Posted ghostant
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉查找树BST相关的知识,希望对你有一定的参考价值。
1.什么是二叉查找树
在ADT MAP的实现方案中,可以采用不同的数据结构与搜索算法来保存和查找key。
- 有序表数据结构+二分查找法
- 散列表数据结构+散列及冲突解决算法
这里我们将尝试用二叉查找树保存key,实现key值的快速搜索。
2.二叉查找树的性质
比父节点小的key都出现在左子树中,比父节点大的key都出现在右子树中,这个性质可以看出二叉查找树时递归性质的。
70
31 93
14 null 73 94
23
31
23 93
14 73 94
70
我们可以看到同一组数据(70,31,93,94,14,23,73)根据插入的顺序不同,生成的bst也是不一样的。
3.二叉树的实现及算法分析
class BinarySearchTree:
def __init__(self):
self.root = None
self.size = 0
def length(self):
return self.size
def __len__(self):
return self.size
def __iter__(self):
return self.root.__iter__()
def put(self,key,val):
if self.root:
self._put(key,val,self.root)
else:
self.root = TreeNode(key,val)
self.size +=1
# 思路:key小于当前节点的key,并且当前节点有左节点,则_put递归调用currentNode的节点为当前currentNode的左节点,如果当前节点没有左节点,则插入的key,val成为当前节点的左节点。同理。
def _put(key,val,currentNode):
if key < currentNode:
if currentNode.hasLeftChild():
self._put(key,val,currentNode.leftChild)
else:
currentNode.leftChild = TreeNode(key,val,parent=currentNode)
else:
if currentNode.hasRightChild:
self._put(key,val,currentNode.rightChild)
else:
currentNode.rightChild = TreeNode(key,val,parent=currentNode)
# tree_obj[k] = v时触发
def __setitem__(self,k,v):
self.put(key,val)
def get(self,key):
# 从根节点开始找
if self.root:
res = self._get(key,self.root)
if res:
return res.payload
else:
return None
else:
return None
def _get(self,key,currentNode):
if not currentNode:
return None
if currentNode.key == key:
return currentNode
elif currentNode.key > key:
return self._get(key,currentNode.leftChild)
else:
return self._get(key,currentNode.rightChild)
def __getitem__(self,key):
return self.get(key)
def __contains__(self,key):
if self._get(key,self.root):
return True
else:
return False
def delete(self,key):
if self.size > 1:
nodeToRemove = self._get(key,self.root)
if nodeToRemove:
self.remove(nodeToremove)
self.size -=1
else:
raise KeyError('Error,key not in tree')
elif self.size == 1 and self.root.key == key:
self.root = None
self.size = 0
else:
raise KeyError('Error,key not in tree')
def __delitem__(self,key):
self.delete(key)
'''
从BST中remove一个节点,还要求仍然保持BST的性质。
分情况讨论:
1.该节点是叶子节点
2.该节点只有左节点或右节点
3.该节点同时有左节点和右节点
'''
def remove(self,currentNode):
if currentNode.isLeaf():
if currentNode == currentNode.parent.leftChild:
currentNode.parent.leftChild = None
else:
currentNode.parent.rightChild = None
# 有两个节点
'''
此时无法就简单地进行替换被删节点,但可以找到另一个合适地节点来替换被删节点,整个合适节点就是被删节 点的下一个key值节点。即被删除节点右子树中做小的那个,称为‘后继’。 这个‘后继’ 只能是叶子节点或者是只有一个右节点。
'''
elif currentNode.hasBothChild():
succ = currentNode.findSuccessor()
succ.spliceOut()
currentNode.key = succ.key
currentNode.payload = succ.payload
# 只有一个节点
else:
if currentNode.hasLeftChild():
if currentNode.isLeftChild(): # 当前节点有一个左节点并且它本身是左节点
currentNode.leftChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.leftChild
elif currentNode.isRightChild():# 当前节点有一个左节点并且它本身是右节点
currentNode.leftChild.parent = currentNode.parent
currentNOde.parent.rightChild = currentNode.rightChild
else: # 当前节点有一个左节点,并且它本身既不是左节点也不是右节点(根节点)
# self.root = currentNode.leftChild
currentNode.replaceNodeData(
currentNode.leftChild.key,
currentNode.leftChild.payload,
currentNode.leftChild.leftChild,
currentNode.leftChild.rightChild)
else:
if currentNode.isLeftChild(): # 当前节点有一个右节点并且它本身是左节点
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.rightChild
elif currentNode.isRightChild(): # 当前节点有一个右节点并且它本身是右节点
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.rightChild = currentNode.rightChild
else:# 当前节点有一个右节点,并且它本身既不是左节点也不是右节点(根节点)
# self.root = currentNode.rightChild
currentNode.replaceNodeData(
currentNode.rightChild.key,
currentNode.rightChild.payload,
currentNode.rightChild.leftChild,
currentNode.rightChild.rightChild)
class TreeNode:
def __init__(self,key,val,left=None,rigth=None,parent=None):
self.key = key
self.payload = val
self.leftChild = left
self.rightChild = right
self.parent = parent
def hasLeftChild(self):
return self.leftChild
def hasRightChild(self):
return self.rightChild
def isLeftChild(self):
return self.parent and self.parent.leftChild == self
def isRightChild(self):
return self.parent and self.parent.rightChild == self
def isRoot(self):
return not self.parent
def isLeaf(self):
return not (self.rightChild or self.leftChild)
def hasAnyChildren(self):
return self.rightChild or self.leftChild
def hasBothChild(self):
return self.leftChild and self.rightChild
def replaceNodeData(self,key,val,lc,rc):
self.key = key
self.payload = value
self.leftChild = lc
self.rightChild = rc
if self.hasLeftChild():
self.leftChild.parent = self
if self.hasRightChild():
self.rightChild.parent = self
# 中序遍历
def __iter__(self):
if self:
if self.hasLeftChild():
# 递归调用
for elem in self.leftChild:
yield elem
yield self.key
if self.hasRihtChild():
for elem in self.rightChild:
yield elem
def findSuccessor(self):
succ = None
if self.hasRightChild():
succ = self.rightChild.findMin()
else: # 其实可以不考虑这个情况,findSuccessor被调用的前提时在当前节点同时有左右节点的情况下。
if self.parent:
if self.isLeftChild():
succ = self.parent
else:
self.parent.rightChild = None
succ = self.parent.findSuccessor()
self.parent.rightChild = self
return succ
def findMin(self):
current = self
# 迭代即可完成
while current.hasLeftChild():
current = current.leftChild
return current
# 通用节点摘除方法
def spliceOut(self):
if self.isLeaf():
if self.isLeftChild():
self.parent.leftChild = None
else:
self.parent.rightChild = None
elif self.hasAnyChild():
if self.hasLeftChild():
if self.isLeftChild():
self.parent.leftChild = self.leftChild
else:
self.parent.rightChild = self.leftChild
self.leftChild.parent = self.parent
else:
if self.isLeftChild():
self.parent.leftChild = self.rightChild
else:
self.parent.rightChild = self.rightChild
self.rightChild.parent = self.parent
4.小结
# 算法分析 (以put为例)
# 其性能决定因素在于二叉搜索树的高度,而高度收到数据项key插入顺序的影响。
# 如果key的列表是随机分布的,那么大于和小于根节点key的键值大致相等,BST的高度就是logn(n是节点个数),而且这样的树就是平衡树,此时put方法最差性能为logn。
# 但是如果key列表分布的极端情况就完全不同。树向一边完全倾斜(类似于单列表的情况)有几个节点高度就为多少。
以上是关于二叉查找树BST的主要内容,如果未能解决你的问题,请参考以下文章
数据结构------------------二叉查找树(BST)的java实现
“中兴捧月”比赛之——二叉查找树(BST)树的最短路径Java求解