为什么要用二分搜索树?
二分搜索树最常用的场景就是查找表的实现,其实可以看成是字典的形式,这样的一对对数据,我们就可以通过键来直接查找到对应的值。进行相应的操作。你可能会想到,这样的key的值,可以直接用数组来存储,但很多时候,键值不一定都是整数,所以这时候实现一个二分搜索树就很明智了。
二分搜索树的优势在哪里呢?
比较一下使用三种方法实现这样一个查找表的时间复杂度
二分搜索树的效率是最高的,而且不仅仅是查找,插入,删除等也是非常高效的,因此,我们可以动态的维护数据,除此之外,还有很多业务上的操作二分查找树也能实现,比如min,max,floor,ceil等。
二分搜索树的定义
二分搜索树也是一颗二叉树,如图所示:
与一般的二叉树不同的是,二分搜索树树具有一下三个性质:
- 若左子树不为空,则左子树上所有节点的值均小于它的根节点的值
- 若右子树不为空,则右子树上所有节点的值均大于它的根节点的值
- 以左右孩子为根的子树依然是二分搜索树
- 没有键值相等的节点
在讲到堆的时候,我曾说堆是一颗完全的二叉树,但二分搜索树并没有这个限制,如下这样的二叉树也是二分搜索树:
因为不是完全的二叉树,所以我们不能再使用数组来存储相应的结构了,在二分搜索树中我们使用节点来存储相应的结构,主要的结构有:
- key
- value
- parent
- left:左孩子
- right:右孩子
二叉搜索树的基本功能
初始化二分搜索树的节点
根据上一节说的二分搜索树的主要特点,我们需要设计一下初始化的函数,方便接下来的插入,删除等操作。
#创建树节点类
class TreeNode(object):
def __init__(self,key,value,parent = None,left=None, right = None):
#初始化
self.key = key
self.value = value
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.leftChild == self
def isRightChild(self):
return self.parent and self.rightChild == self
def isRoot(self):
return not self.parent
def isLeaf(self):
return not (self.leftChild and self.rightChild)
插入元素
如图所示:
假如要插入一个元素60,比较一下待插入元素(60)和根节点的元素(41)的大小,如果60>41,则判断一下41这个当前节点是否有右孩子(维护性质),如果有,则把右孩子更新为当前节点,做一个递归的操作;如果没有,直接插入到41这个元素右孩子所在位置。
详见python代码:
class BST(object):
def __init__(self):
self.size = 0
self.root = None
def __len__(self):
return self.size
def length(self):
return self.size
def insert(self,key,value):
if self.root:
self._insert(key,value,self.root)
else:
self.root = TreeNode(key,value)
self.size += 1
return self.root
def _insert(self,key,value,currentNode):
if key < currentNode.key:
if currentNode.hasLeftChild():
self._insert(key,value,currentNode.leftChild)
else:
currentNode.leftChild = TreeNode(key,value,parent=currentNode)
elif key == currentNode.key:
currentNode.value = value
else:
if currentNode.hasRightChild():
self._insert(key,value,currentNode.rightChild)