0.二分搜索树
二分搜索树(Binary Search Tree,BST)是一颗典型的二叉树,任何节点的键值大于等于该节点左子树中的所有键值,小于等于该节点右子树中的所有键值。 二叉搜索树可以看作是有序集合的树形存储,是一种用途非常广泛的基础数据结构。
1.二分搜索树的优势
二分搜索树不仅能够高效的查找数据,还可以高效的插入和删除数据。时间复杂度都为O(logn)。
2.二分搜索树的操作
2.1 插入
插入操作是一个不断递归的过程,假设现在有一棵二分搜索树如下图所示:
现在,想要插入一个新的元素,这个新的元素的key为60
这时候,新元素的key和树的根节点的key作比较,60>41,所以新节点要插入到根结点的右子树。
同样,60>58,需要插入到58这个节点的右子树中,所以最后结果如下:
2.2 查找
查找操作和插入操作类似,假设现在要查找key为42的节点
因为42比41到,所以要到41的右子树中查找。
现在42比58小,所以要打58的左子树种查找。
然后42比50小,就到50的左子树种查找,欸,发现找到了42,查找操作结束。
2.3 广度优先遍历
广度优先遍历也就是层次优先遍历,遍历顺序是一层一层地从左到右的遍历方式。假设有如下图所示的二分搜索树,那么遍历结果应该为:28,16,30,13,22,29,42。
为了实现这种遍历,需要借助一个辅助的数据结构—-队列。如果现在要做广度优先遍历,那么先把根结点入队。
然后把28出队,并把28的左右孩子节点入队。
然后把16和30出队,并把16和30的孩子节点入队。
然后把13,22,29,42出队,由于他们都没有孩子节点了,所以遍历也就结束了。
2.4 删除最小值和最大值节点
由于二分搜索树的性质,很容易能想到最小值的节点,就是树的最左边的叶子节点,而最大值的节点是最右边的叶子节点。在删除最大值或最小值节点时,当然不能直接把最小值或最大值节点删掉就好了,因为如果此时最小值节点有右孩子或最大值节点有左孩子,那么这样删除会有问题。如下图中13是最小值节点,他没有右孩子,所以直接删除这个节点就行了,但是最大值节点58有左孩子,直接删除58这个节点是不行的。
像这种情况,就需要将58的左子树作为41的右子树进行连接。
2.5 删除任意节点
如果需要删除的节点没有孩子或者只有孩子,那么思路和2.4中删除最小值和最大值节点的思路是一样的,但是如果待删除节点的左右孩子节点都存在的话应该怎么办呢?
假设现在要删除这棵树的58节点叫d。
那么我们同样需要找一个节点来代替58,但这个节点不应该是50或者60,而应该是右孩子的左孩子(59)。因为58会大于它的左孩子,小于它的右孩子。也就是说58的右子树的所有节点也同样都大于58的左子树的所有节点。所以找替换节点在58的右子树中找没毛病,但也不能瞎找。应该找58的右子树种的最小值节点。也就是59。
我们也可以比较59为s,其实s是d的后继节点。
现在找到了待删除节点d的后继节点s,接下来只需把s删掉,替换到d的位置,然后把d删除即可。
3 代码实现
template <typename Key, typename Value>
class BST{
private:
struct Node{
Key key;
Value value;
Node *left;
Node *right;
Node(Key key, Value value){
this->key = key;
this->value = value;
this->left = this->right = NULL;
}
Node(Node* node)
{
this->key = node->key;
this->value = node->value;
this->left = node->left;
this->right = node->right;
}
};
Node *root;
int count;
public:
BST(){
root = NULL;
count = 0;
}
~BST(){
destory(root);
}
int size(){
return count;
}
bool isEmpty(){
return count == 0;
}
void insert(Key key, Value value){
root = insert(root, key, value);
}
bool contain(Key key){
return contain(root, key);
}
Value* search(Key key){
return search(root, key);
}
void remove(Key key)
{
root = remove(root, key);
}
void level_order()
{
queue<Node*> queue;
queue.push(root);
while (!queue.empty())
{
Node* node = queue.front();
queue.pop();
cout << node->key << endl;
if (node->left)
queue.push(node->left);
if (node->right)
queue.push(node->right);
}
}
Key minmum()
{
Node* node = root;
while (node->left)
{
node = node->left;
}
return node->key;
}
Key maxmum()
{
Node* node = root;
while (node->right)
{
node = node->right;
}
return node->key;
}
void remove_min()
{
if (!root)
return;
root = remove_min(root);
}
void remove_max()
{
if (!root)
return;
root = remove_max(root);
}
private:
Node* minmum(Node* node)
{
if (!node)
return nullptr;
while (node->left)
{
node = node->left;
}
return node;
}
Node* maxmum(Node* node)
{
if (!node)
return nullptr;
while (node->right)
{
node = node->right;
}
return node;
}
Node* remove(Node* node,Key key)
{
if (node == nullptr)
{
return nullptr;
}
if (key < node->key)
{
node->left = remove(node->left, key);
return node;
}
else if (key > node->key)
{
node->right = remove(node->right, key);
return node;
}
else
{
if (node->left == nullptr)
{
Node* right = node->right;
delete node;
count--;
return right;
}
if (node->right == nullptr)
{
Node* left = node->left;
delete node;
count--;
return left;
}
Node* d = node;
Node* s = new Node(minmum(d->right));
++count;
s->right = remove_min(node->right);
s->left = node->left;
delete d;
--count;
return s;
}
}
Node* remove_min(Node* node)
{
if (node->left == nullptr)
{
Node* right_node = node->right;
delete node;
count--;
return right_node;
}
node->left = remove_min(node->left);
return node;
}
Node* remove_max(Node* node)
{
if (node->right == nullptr)
{
Node* left_node = node->left;
delete node;
count--;
return left_node;
}
node->right = remove_max(node->right);
return node;
}
void destory(Node* node)
{
if (node)
{
destory(node->left);
destory(node->right);
delete node;
--count;
}
}
// 向以node为根的二叉搜索树中,插入节点(key, value)
// 返回插入新节点后的二叉搜索树的根
Node* insert(Node *node, Key key, Value value){
if (node == NULL){
count++;
return new Node(key, value);
}
if (key == node->key)
node->value = value;
else if (key < node->key)
node->left = insert(node->left, key, value);
else // key > node->key
node->right = insert(node->right, key, value);
return node;
}
// 查看以node为根的二叉搜索树中是否包含键值为key的节点
bool contain(Node* node, Key key){
if (node == NULL)
return false;
if (key == node->key)
return true;
else if (key < node->key)
return contain(node->left, key);
else // key > node->key
return contain(node->right, key);
}
// 在以node为根的二叉搜索树中查找key所对应的value
Value* search(Node* node, Key key){
if (node == NULL)
return NULL;
if (key == node->key)
return &(node->value);
else if (key < node->key)
return search(node->left, key);
else // key > node->key
return search(node->right, key);
}
};