1 定义
- 如果左子树不为空,且左子树上所有节点的值均小于根节点的值。
- 如果右子树不为空,则右子树上所有节点的值均大于根节点的值。
- 左、右子树也都是二叉查找树。
总结:就是从左、根、右具有单调递增的二叉树就是二叉查找树。
时间复杂度:O(logn)和树的层级成正比。
2 有序性
二叉查找树不仅可以用于查找,还可以维持节点的有序性。
对二叉查找树使用中序遍历的操作,访问左子树、根节点、右子树,出来的结果就是一个具有单调递增性质的数据。所以二叉查找树又被称为二叉排序树。
3 查找、插入
二叉查找树的查找和插入的过程基本一致,都是从根节点依次和查找【插入】的数据比较大小,然后判断查找【插入】的位置。
4 代码实现
/**
查找
*/
func BinaryTreeSearch(rootNode *Node, data int) (targetNode *Node) {
if rootNode == nil { //如果根节点为nil,则为搜索失败,即targetNode=nil
targetNode = nil
} else {
if rootNode.Data == data {
targetNode = rootNode
} else if rootNode.Data > data {
targetNode = BinaryTreeSearch(rootNode.Left, data) //递归搜索左子树
} else if rootNode.Data < data {
targetNode = BinaryTreeSearch(rootNode.Right, data) //递归搜索右子树
}
}
return targetNode
}
/**
插入
*/
func InsertElement(rootNode *Node, data int) (bool, error) {
insertResult := false
err := errors.New("插入失败")
insertNode := &Node{
Data: data,
Left: nil,
Right: nil,
}
if rootNode == nil { //根节点为nil时直接插入到根节点处
rootNode = insertNode
insertResult = true
} else {
if rootNode.Data == data {
insertResult = false
err = errors.New("二叉查找树中已存在该元素")
} else if rootNode.Data > data { //根节点的值大于待插入的值
if rootNode.Left != nil { //判断是否有左孩子,若有则继续递归左孩子,若无则直接插入到左孩子位置
insertResult, err = InsertElement(rootNode.Left, data)
} else {
rootNode.Left = insertNode
insertResult = true
}
} else if rootNode.Data < data { //根节点的值小于待插入的值
if rootNode.Right != nil { //判断是否有右孩子,若有则继续递归右孩子,若无则直接插入到右孩子位置
insertResult, err = InsertElement(rootNode.Right, data)
} else {
rootNode.Right = insertNode
insertResult = true
}
}
}
return insertResult, err
}
5 删除
二叉查找树的删除有三种情况:
1、待删除的节点是叶子节点,删除后不需要调整树的结构,可以直接删除。
2、待删除的节点是根节点,有一个孩子节点【左孩子结点】,节点删除后,直接将其左孩子节点放到待删除节点的位置即可。
3、待删除节点有两个子节点,一般从其右子树开始递归到最底层的左叶子节点放到待删除节点的位置。【叙述上仍需完善】
5.1 代码实现
/**
删除
*/
func DeleteElement(rootNode *Node, data int) bool {
//先找到当前节点的父节点
deleteResult := false
parentNode := &Node{ //待删除的元素构成的节点
Data: data,
Left: nil,
Right: nil,
}
targetNode := rootNode
for targetNode.Data != data { //当前节点的值不等于待删除的值时,将当前节点作为父节点
parentNode = targetNode
if data > targetNode.Data {
targetNode = targetNode.Right
} else {
targetNode = targetNode.Left
}
if targetNode == nil {
deleteResult = false
}
}
// 待删除结点没有子节点
if targetNode.Right == nil && targetNode.Left == nil {
if targetNode == rootNode {
//待删除结点是根结点
rootNode = nil
} else {
if parentNode.Right == targetNode {
parentNode.Right = nil
} else {
parentNode.Left = nil
}
}
} else if targetNode.Left == nil { //待删除结点有一个子结点(右)
if targetNode == rootNode {
rootNode = targetNode.Right
} else if parentNode.Right == targetNode {
parentNode.Right = targetNode.Right
} else {
parentNode.Left = targetNode.Right
}
} else if targetNode.Right == nil { //待删除结点有一个子结点(左)
if targetNode == rootNode {
rootNode = targetNode.Left
} else if parentNode.Right == targetNode {
parentNode.Right = targetNode.Left
} else {
parentNode.Left = targetNode.Left
}
} else { //待删除结点有两个子结点
//待删除结点的后继结点的父结点
successParentNode := targetNode
//待删除结点的后继结点
successNode := targetNode.Right
for successNode.Left != nil {
successParentNode = successNode
successNode = successNode.Left
}
//把后继结点复制到待删除结点位置
targetNode.Data = successNode.Data
//删除后继结点
if successParentNode.Right == successNode {
successParentNode.Right = successNode.Right
} else {
successParentNode.Left = successNode.Right
}
}
return deleteResult
}
6 时间/空间复杂度
参考1:二叉排序树的时间复杂度
如果二叉排序树是平衡的,则n个节点的二叉排序树的高度为Log 2n+1,其查找效率为O(Log 2n),近似于折半查找。如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。一般的,二叉排序树的查找性能在O(Log 2n)到O(n)之间。因此,为了获得较好的查找性能,就要构造一棵平衡的二叉排序树。