- 二叉搜索树的概念
- 二叉搜索树的操作
- 二叉搜索树的实现
- 二叉搜索树的应用
一.二叉搜索树的概念
二叉搜索树(BST),又称为二叉排序树,它具有以下性质
- 若左子树不为空,任何一个根节点比它的左子树节点都大
- 若右子树不为空,任何一个根节点比它的右子树节点都小
- 左右子树都为二叉搜索树
二.二叉搜索树的操作
1.插入
树为空,则直接新增节点,赋值给root指针,树不空,按二叉搜索树性质查找插入位置,插入新节点
2.查找
从根开始比较,比根大的往右边查找,比根小的往左边查找。最多查找高度次,走到空说明值不存在
3.删除
先按照二叉搜索树的性质查找删除节点,不存在则返回,否则进行删除:
a. 要删除的节点无孩子节点(可以直接删除)
b. 要删除的节点只有一个孩子节点(要托管给父亲)
c. 要删除的节点有两个孩子,在删除时,必须保证树结构不变,所以不可以直接删除,只能间接删除,找到该节点左树中的最大节点或者右树中的最小节点,然后交换两个节点,此时就转换到了a或者b这两种情况
三.二叉搜索树的实现
#pragma once
template<class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> node;
public:
//因为有了拷贝构造函数,所以编译器不会自动生成默认构造,default就是强制默认生成
BSTree() = default;
//拷贝构造
BSTree(const BSTree<K>& bt)
{
_root = copy(bt._root);
}
~BSTree()
{
destroy(_root);
}
BSTree<K>& operator=(BSTree<K> t)
{
swap(t._root, _root);
return *this;
}
//循环实现插入
bool Insert(const K& key)
{
//如果根为空,直接插入
if (_root == nullptr)
{
_root = new node(key);
return true;
}
node* parent = nullptr;
node* cur = _root;
while (cur != nullptr)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new node(key);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
//递归实现插入
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
//中序遍历
void InOrder()
{
_InOrder(_root);
}
//循环实现查找
bool find(const K& key)
{
node* cur = _root;
while (cur != nullptr)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
return true;
}
}
return false;
}
//循环实现删除
bool erase(const K& key)
{
node* parent = nullptr;
node* cur = _root;
while (cur != nullptr)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else 找到了目标节点
{
//找到目标节点,进行删除,需要分三种情况(没有孩子节点,只有一个孩子节点,两个孩子节点都有),但实际只需分两种情况
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_root == cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
}
else
{
//有两个孩子节点,需要找出右边最小/左边最大,这样就可以保证删除后结构不改变
node* pminRight = cur;
node* minRight = cur->_right;
while (minRight->_left != nullptr)
{
pminRight = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (pminRight == cur)
{
pminRight->_right = minRight->_right;
}
else
{
pminRight->_left = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}
//递归实现删除
bool EraseR(const K& key)
{
if (_root == nullptr)
{
return false;
}
return _EraseR(_root, key);
}
//递归实现查找
bool FindR(const K& key)
{
return _root != nullptr && _FindR(_root, key);
}
protected:
node* copy(const node* root)
{
if (root == nullptr)
{
return nullptr;
}
node* new_node = new node(root->_key);
new_node->_left = copy(root->_left);
new_node->_right = copy(root->_right);
return new_node;
}
void destroy(node*& root)
{
if (root == nullptr)
{
return;
}
destroy(root->_left);
destroy(root->_right);
delete root;
root = nullptr;
}
void _InOrder(node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
bool _EraseR(node*& root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (root->_key < key)
{
return _EraseR(root->_right, key);
}
if (root->_key > key)
{
return _EraseR(root->_left, key);
}
node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
//node* pmaxLeft = root;
node* maxLeft = root->_left;
while (maxLeft->_right != nullptr)
{
//pmaxLeft = maxLeft;
maxLeft = maxLeft->_right;
}
swap(maxLeft->_key, root->_key);
//del = maxLeft;
/*if (pmaxLeft->_left == maxLeft)
{
pmaxLeft->_left = maxLeft->_left;
}
else
{
pmaxLeft->_right = maxLeft->_left;
}*/
return _EraseR(root->_left, key);
}
delete del;
return true;
}
bool _FindR(const node*& root, const K& key)
{
if (root == nullptr)
{
return false;
}
if (root->_key < key)
{
return _FindR(root->_right, key);
}
else if (root->_key > key)
{
return _FindR(root->_left, key);
}
else
{
return true;
}
}
bool _InsertR(node*& root, const K& key)
{
if (root == nullptr)
{
root = new node(key);
return true;
}
if (root->_key < key)
{
return _InsertR(root->_right, key);
}
else if (root->_key > key)
{
return _InsertR(root->_left, key);
}
else
{
return false;
}
}
private:
node* _root = nullptr;
};
:::tips
二叉搜索树有一个缺陷,它可能是一颗单分支树,这种情况时间复杂度不能视为O(lgn),只能按照O(n)。平衡搜索二叉树可以消除这一缺陷。
:::
四.二叉搜索树的应用
1.k模型
K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
- 只要涉及‘在不在’的问题,都是k模型,比如,人脸识别系统。
- set容器就是k模型
2.kv模型
每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。
- 比如:统计单词出现次数,翻译