肝!
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
性质如下:
- 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 任意节点的左、右子树也分别为二叉查找树。
- 没有键值相等的节点。
二叉排序树的查找过程和次优二叉树类似,通常采取二叉链表作为二叉排序树的存储结构。中序遍历二叉排序树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。每次插入的新的结点都是二叉排序树上新的叶子结点,在进行插入操作时,不必移动其它结点,只需改动某个结点的指针,由空变为非空即可。搜索,插入,删除的复杂度等于树高,O(log(n)).
当二叉搜索树出现如下图情形时,效率最低:
class BST(object):
def __init__(self, root=None):
self.root = root
@classmethod
def build_from(cls, node_list):
cls.size = 0
key_to_node_dict = {}
for node_dict in node_list:
key = node_dict['key']
key_to_node_dict[key] = BSTNode(key, value=key) # 这里值暂时用 和 key一样的
for node_dict in node_list:
key = node_dict['key']
node = key_to_node_dict[key]
if node_dict['is_root']:
root = node
node.left = key_to_node_dict.get(node_dict['left'])
node.right = key_to_node_dict.get(node_dict['right'])
cls.size += 1
return cls(root)
NODE_LIST = [
{'key': 60, 'left': 12, 'right': 90, 'is_root': True},
{'key': 12, 'left': 4, 'right': 41, 'is_root': False},
{'key': 4, 'left': 1, 'right': None, 'is_root': False},
{'key': 1, 'left': None, 'right': None, 'is_root': False},
{'key': 41, 'left': 29, 'right': None, 'is_root': False},
{'key': 29, 'left': 23, 'right': 37, 'is_root': False},
{'key': 23, 'left': None, 'right': None, 'is_root': False},
{'key': 37, 'left': None, 'right': None, 'is_root': False},
{'key': 90, 'left': 71, 'right': 100, 'is_root': False},
{'key': 71, 'left': None, 'right': 84, 'is_root': False},
{'key': 100, 'left': None, 'right': None, 'is_root': False},
{'key': 84, 'left': None, 'right': None, 'is_root': False},
]
bst = BST.build_from(NODE_LIST)
BST操作——查找
如何查找一个指定的节点呢,根据定义我们知道每个内部节点左子树的 key 都比它小,右子树的 key 都比它大,所以 对于带查找的节点 search_key,从根节点开始,如果 search_key 大于当前 key,就去右子树查找,否则去左子树查找。 一直到当前节点是 None 了说明没找到对应 key。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1v7f71jQ-1630486310564)(C:\Users\Vermouth\AppData\Roaming\Typora\typora-user-images\image-20210901143632179.png)]
好,撸代码:
def _bst_search(self, subtree, key):
if subtree is None: # 没找到
return None
elif key < subtree.key:
return self._bst_search(subtree.left, key)
elif key > subtree.key:
return self._bst_search(subtree.right, key)
else:
return subtree
def get(self, key, default=None):
node = self._bst_search(self.root, key)
if node is None:
return default
else:
return node.value
BST操作——获取最大和最小key的节点
其实还按照其定义,最小值就一直向着左子树找,最大值一直向右子树找,递归查找就行
def _bst_min_node(self, subtree):
if subtree is None:
return None
elif subtree.left is None: # 找到左子树的头
return subtree
else:
return self._bst_min_node(subtree.left)
def bst_min(self):
node = self._bst_min_node(self.root)
return node.value if node else None
BST操作——插入
插入节点的时候我们需要一直保持 BST 的性质,每次插入一个节点,我们都通过递归比较把它放到正确的位置。 你会发现新节点总是被作为叶子结点插入。(请你思考这是为什么)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QzIOXTvS-1630486310568)(C:\Users\Vermouth\AppData\Roaming\Typora\typora-user-images\image-20210901144956021.png)]
def _bst_insert(self, subtree, key, value):
""" 插入并且返回根节点
:param subtree:
:param key:
:param value:
"""
if subtree is None: # 插入的节点一定是根节点,包括 root 为空的情况
subtree = BSTNode(key, value)
elif key < subtree.key:
subtree.left = self._bst_insert(subtree.left, key, value)
elif key > subtree.key:
subtree.right = self._bst_insert(subtree.right, key, value)
return subtree
def add(self, key, value):
node = self._bst_search(self.root, key)
if node is not None: # 更新已经存在的 key
node.value = value
return False
else:
self.root = self._bst_insert(self.root, key, value)
self.size += 1
return True
BST操作——删除节点
删除操作相比上边的操作要麻烦很多,首先需要定位一个节点,删除节点后,我们需要始终保持 BST 的性质。 删除一个节点涉及到三种情况:
我们分别来看看三种情况下如何删除一个节点:
这是最简单的一种情况,只需要把它的父亲指向它的指针设置为 None 就好。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f01WwcUF-1630486310571)(C:\Users\Vermouth\AppData\Roaming\Typora\typora-user-images\image-20210901145934711.png)]
删除有一个孩子的节点时,我们拿掉需要删除的节点,之后把它的父亲指向它的孩子就行,因为根据 BST 左子树都小于节点,右子树都大于节点的特性,删除它之后这个条件依旧满足。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UmuWNnyN-1630486310573)(C:\Users\Vermouth\AppData\Roaming\Typora\typora-user-images\image-20210901150016218.png)]
假如我们想删除 12 这个节点改怎么做呢?你的第一反应可能是按照下图的方式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JN4w2qWx-1630486310574)(C:\Users\Vermouth\AppData\Roaming\Typora\typora-user-images\image-20210901150049216.png)]
但是这种方式可能会影响树的高度,降低查找的效率。这里我们用另一种非常巧妙的方式。 还记得上边提到的吗,如果你中序遍历 BST 并且输出每个节点的 key,你会发现就是一个有序的数组。 [1 4 12 23 29 37 41 60 71 84 90 100]
。这里我们定义两个概念,逻辑前任(predecessor)和后继(successor),请看下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jXDgcAiK-1630486310575)(C:\Users\Vermouth\AppData\Roaming\Typora\typora-user-images\image-20210901150111136.png)]
12 在中序遍历中的逻辑前任和后继分别是 4 和 23 节点。于是我们还有一种方法来删除 12 这个节点:
说白了就是找到后继并且替换,这里之所以能保证这种方法是正确的,你会发现替换后依旧是保持了 BST 的性质。 有个问题是如何找到后继节点呢?待删除节点的右子树的最小的节点不就是后继嘛,上边我们已经实现了找到最小 key 的方法了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gAbTCgE4-1630486310576)(C:\Users\Vermouth\AppData\Roaming\Typora\typora-user-images\image-20210901150133772.png)]
我们开始编写代码实现,和之前的操作类似,我们还是通过辅助函数的形式来实现,这个递归函数会比较复杂,请你仔细理解:
def _bst_remove(self, subtree, key):
"""删除节点并返回根节点"""
if subtree is None:
return None
elif key < subtree.key:
subtree.left = self._bst_remove(subtree.left, key)
return subtree
elif key > subtree.key:
subtree.right = self._bst_remove(subtree.right, key)
return subtree
else: # 找到了需要删除的节点
if subtree.left is None and subtree.right is None: # 叶节点,返回 None 把其父亲指向它的指针置为 None
return None
elif subtree.left is None or subtree.right is None: # 只有一个孩子
if subtree.left is not None:
return subtree.left # 返回它的孩子并让它的父亲指过去
else:
return subtree.right
else: # 俩孩子,寻找后继节点替换,并从待删节点的右子树中删除后继节点
successor_node = self._bst_min_node(subtree.right)
subtree.key, subtree.value = successor_node.key, successor_node.value
subtree.right = self._bst_remove(subtree.right, successor_node.key)
return subtree
def remove(self, key):
assert key in self
self.size -= 1
return self._bst_remove(self.root, key)
完整代码你可以在本章的 bst.py 找到。 另外推荐一个可以在线演示过程的网址大家可以手动执行下看看效果: https://www.cs.usfca.edu/~galles/visualization/BST.html
上边介绍的操作时间复杂度和二叉树的形状有关。平均来说时间复杂度是和树的高度成正比的,树的高度 h 是 log(n), 但是最坏情况下以上操作的时间复杂度都是 O(n)。为了改善 BST 有很多变种,感兴趣请参考延伸阅读中的内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DLK126iH-1630486310577)(C:\Users\Vermouth\AppData\Roaming\Typora\typora-user-images\image-20210901150630137.png)]
验证是否是合法二叉搜索树 [validate-binary-search-tree](https://leetcode.com/problems/validate-binary-search-tree/
#include
using namespace std;
//二叉搜索树结构
template
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
V _value;
BSTreeNode(const K& key, const V& value)
:_left(NULL)
,_right(NULL)
,_key(key)
,_value(value)
{}
};
template
class BSTree
{
typedef BSTreeNode Node;
public:
BSTree()
:_root(NULL)
{}
//在二叉搜索树中插入节点
bool Insert(const K& key, const V& value)
{
if (_root == NULL)
{
_root = new Node(key, value);
}
Node* cur=_root;
Node* parent = NULL;
//首先找到要插入的位置
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if(cur->_key_right;
}
else
{
return false;
}
}
//在找到插入位置以后,判断插入父亲节点的左边还是右边
if (parent->_key > key)
{
parent->_left = new Node(key, value);
}
else
{
parent->_right = new Node(key, value);
}
return true;
}
//在二叉搜索树中查找节点
Node* Find(const K& key)
{
Node* cur=_root;
//开始遍历查找
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if(cur->_key_right;
}
else
{
return cur;
}
}
return NULL;
}
//在二叉搜索树中删除节点
bool Remove(const K& key)
{
//没有节点
if (_root == NULL)
{
return false;
}
//只有一个节点
if (_root->_left == NULL&&_root->_right == NULL)
{
if (_root->_key == key)
{
delete _root;
_root = NULL;
return true;
}
return false;
}
Node* parent = NULL;
Node* cur = _root;
//遍历查找要删除节点的位置
while (cur)
{
Node* del = NULL;
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
//要删除节点的左子树为空,分3种情况
if (cur->_left == NULL)
{
//注意判断父节点是否为空,若为空,则要删除的节点为根节点,如:只有根节点5和其右节点9
if (parent == NULL)
{
_root = cur->_right;
delete cur;
cur = NULL;
return true;
}
if (parent->_key > cur->_key)
{
del = cur;
parent->_left = cur->_right;
delete del;
return true;
}
else if (parent->_key < key)
{
del = cur;
parent->_right = cur->_right;
delete del;
return true;
}
}
//要删除节点的右子树为空,同样分3种情况
else if (cur->_right == NULL)
{
//注意判断父节点是否为空,若为空,则要删除的节点为根节点,如:只有根节点5和其左节点3
if (parent == NULL)
{
_root = cur->_left;
delete cur;
cur = NULL;
return true;
}
if (parent->_key > cur->_key)
{
del = cur;
parent->_left = cur->_left;
delete del;
return true;
}
else if (parent->_key < cur->_key)
{
del = cur;
parent->_right = cur->_left;
delete del;
return true;
}
}
//左右子树都不为空
else
{
Node* del = cur;
Node* parent = NULL;
Node* RightFirst = cur->_right;
//右边第一个节点的左子树为空
if (RightFirst->_left == NULL)
{
swap(RightFirst->_key, cur->_key);
swap(RightFirst->_value, cur->_value);
del = RightFirst;
cur->_right = RightFirst->_right;
delete del;
return true;
}
//右边第一个节点的左子树不为空
while (RightFirst->_left)
{
parent = RightFirst;
RightFirst = RightFirst->_left;
}
swap(RightFirst->_key, cur->_key);
swap(RightFirst->_value, cur->_value);
del = RightFirst;
parent->_left = RightFirst->_right;
delete del;
return true;
}
}
}
return false;
}
bool Insert_R(const K& key, const V& value)
{
return _Insert_R(_root, key, value);
}
Node* Find_R(const K& key)
{
return _Find_R(_root, key);
}
bool Remove_R(const K& key)
{
return _Remove_R(_root, key);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
protected:
bool _Remove_R(Node*& root, const K& key)
{
//没有节点
if (root == NULL)
{
return false;
}
//只有一个节点
if (root->_left == NULL&&root->_right == NULL)
{
if (root->_key == key)
{
delete root;
root = NULL;
return true;
}
else
{
return false;
}
}
//删除二叉搜索树节点的递归写法
if (root->_key > key)
{
_Remove_R(root->_left, key);
}
else if (root->_key < key)
{
_Remove_R(root->_right, key);
}
else
{
Node* del = NULL;
if (root->_left == NULL)
{
del = root;
root = root->_right;
delete del;
del = NULL;
return true;
}
else if (root->_right == NULL)
{
del = root;
root = root->_left;
delete del;
del = NULL;
return true;
}
else
{
Node* RightFirst = root->_right;
while (RightFirst->_left)
{
RightFirst = RightFirst->_left;
}
swap(root->_key, RightFirst->_key);
swap(root->_value, RightFirst->_value);
_Remove_R(root->_right, key);
return true;
}
}
}
//递归查找法
Node* _Find_R(Node* root, const K& key)
{
if (root == NULL)
{
return NULL;
}
if (root->_key > key)
{
return _Find_R(root->_left, key);
}
else if (root->_key < key)
{
return _Find_R(root->_right, key);
}
else
{
return root;
}
}
//递归插入法
bool _Insert_R(Node*& root, const K& key, const V& value)
{
if (root == NULL)
{
root = new Node(key, value);
return true;
}
if (root->_key > key)
{
return _Insert_R(root->_left, key, value);
}
else if(root->_key < key)
{
return _Insert_R(root->_right, key, value);
}
else
{
return false;
}
}
void _InOrder(Node* root)
{
if (root == NULL)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
protected:
Node* _root;
};
void Test()
{
BSTree s;
//测试插入
s.Insert_R(5, 1);
s.Insert_R(4, 1);
s.Insert_R(3, 1);
s.Insert_R(6, 1);
s.Insert_R(1, 1);
s.Insert_R(2, 1);
s.Insert_R(0, 1);
s.Insert_R(9, 1);
s.Insert_R(8, 1);
s.Insert_R(7, 1);
//二叉搜索树按中序输出是有序的
s.InOrder();
//测试查找
cout << s.Find_R(6)->_key << endl;
//测试删除
s.Remove(4);
s.Remove(6);
s.Remove(3);
s.Remove(1);
s.Remove(2);
//再次打印删除后的结果
s.InOrder();
}
int main()
{
Test();
system("pause");
return 0;
}
Python代码实现:
class BSTNode(object):
def __init__(self, key, value, left=None, right=None):
self.key, self.value, self.left, self.right = key, value, left, right
class BST(object):
def __init__(self, root=None):
self.root = root
@classmethod
def build_from(cls, node_list):
cls.size = 0
key_to_node_dict = {}
for node_dict in node_list:
key = node_dict['key']
key_to_node_dict[key] = BSTNode(key, value=key) # 这里值暂时用 和 key
for node_dict in node_list:
key = node_dict['key']
node = key_to_node_dict[key]
if node_dict['is_root']:
root = node
node.left = key_to_node_dict.get(node_dict['left'])
node.right = key_to_node_dict.get(node_dict['right'])
cls.size += 1
return cls(root)
'''查找'''
def _bst_search(self, subtree, key):
if subtree is None: # 没找到
return None
elif key < subtree.key:
return self._bst_search(subtree.left, key)
elif key > subtree.key:
return self._bst_search(subtree.right, key)
else:
return subtree
def get(self, key, default=None):
node = self._bst_search(self.root, key)
if node is None:
return default
else:
return node.value
'''获取最大和最小key节点'''
def _bst_min_node(self, subtree):
if subtree is None:
return None
elif subtree.left is None: # 找到左子树的头
return subtree
else:
return self._bst_search(subtree.left)
'''插入'''
def _bst_insert(self, subtree, key, value):
""" 插入并返回根节点
:param subtree:
:param key:
:param value:
:return:
"""
if subtree is None: # 插入的节点一定是跟节点,包括root为空的情况
subtree = BSTNode(key, value)
elif key < subtree.key:
subtree.left = self._bst_insert(subtree.left, key, value)
elif key > subtree.key:
subtree.right = self._bst_insert(subtree.right, key)
return subtree
def add(self, key, value):
node = self._bst_search(self.root, key)
if node is not None: # 更新已经存在的key
node.value = value
return False
else:
self.root = self._bst_insert(self.root, key, value)
self.size += 1
return True
'''删除节点'''
def _bst_remove(self, subtree, key):
"""删除节点并返回根节点"""
if subtree is None:
return None
elif key < subtree.key:
subtree.left = self._bst_remove(subtree.left, key)
return subtree
elif key > subtree.key:
subtree.right = self._bst_remove(subtree.right, key)
return subtree
else: # 找到了需要删除的节点
if subtree.left is None and subtree.right is None: # 叶节点,返回None 把其父亲指向它的指针置为None
return None
elif subtree.left is None or subtree.right is None: # 只有一个孩子
if subtree.left is not None:
return subtree.left
else:
return subtree.right
else: #俩孩子,寻找后继节点替换,并从待删节点的右子树中删除后继节点
successor_node = self._bst_min_node(subtree.right)
subtree.key, subtree.value = successor_node.key, successor_node.value
subtree.right = self._bst_remove(subtree.right, successor_node.key)
return subtree
def remove(self,key):
assert key in self
self.size -= 1
return self._bst_remove(self.root, key)
NODE_LIST = [
{'key': 60, 'left': 12, 'right': 90, 'is_root': True},
{'key': 12, 'left': 4, 'right': 41, 'is_root': False},
{'key': 4, 'left': 1, 'right': None, 'is_root': False},
{'key': 1, 'left': None, 'right': None, 'is_root': False},
{'key': 41, 'left': 29, 'right': None, 'is_root': False},
{'key': 29, 'left': 23, 'right': 37, 'is_root': False},
{'key': 23, 'left': None, 'right': None, 'is_root': False},
{'key': 37, 'left': None, 'right': None, 'is_root': False},
{'key': 90, 'left': 71, 'right': 100, 'is_root': False},
{'key': 71, 'left': None, 'right': 84, 'is_root': False},
{'key': 100, 'left': None, 'right': None, 'is_root': False},
{'key': 84, 'left': None, 'right': None, 'is_root': False},
]
bst = BST.build_from(NODE_LIST)
二叉查找树