目录
一、搜索二叉树
1、搜索二叉树的概念
搜索二叉树又称二叉排序树,它或者是一颗空树,或者是具有以下特征的二叉树:
·若它的左子树不为空,则左子树上的所有节点的值都小于根节点的值
·若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
·它的左右子树也分别为搜索二叉树
2、搜索二叉树的操作
(1)搜索二叉树的查找
a.从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b.最多查找高度次,走到空,还没有找到,这个值不存在。
(2)搜索二叉树的插入
a.树为空,则直接新增节点,赋值给root指针。
b.树不为空,按搜索二叉树性质查找插入位置,插入新节点。
(3)搜索二叉树的删除
首先查找元素是否在搜索二叉树中,如果不存在,则返回,否则要删除的节点可能分下面四种情况:
a.要删除的节点无孩子节点
b.要删除的节点只有左孩子节点
c.要删除的节点只有右孩子节点
d.要删除的节点有左右孩子节点
真正的删除过程有如下几种情况
情况b:删除该节点且使被删除节点的双亲节点指向被删除节点的左孩子节点——直接删除
情况c:删除该节点且使被删除节点的双亲节点指向被删除节点的右孩子节点——直接删除
情况d:在它的右子树中寻找中序下的第一个节点(关键值最小),用它的值填补到被删除节点中,再来处理该节点的删除问题——替换法删除
二、搜索二叉树的模拟实现
template<class K>
struct BSTNode
{
BSTNode<K>* _left;
BSTNode<K>* _right;
K _key;
BSTNode(const K& key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{}
};
template<class K>
class BST
{
typedef BSTNode<K> Node;
public:
BST() = default; //构造函数
BST(const BST<K>& t)//拷贝构造
{
_root = _Copy(t._root);
}
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
//先找到应该插入的位置
while (cur)
{
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 Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
//开始删除
//1、左为空
//2、右为空
//3、左右都不为空
if (cur->_left == nullptr)//左为空
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_right;//因为左为空所以父节点的左指向删除节点的右
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
cur = nullptr;
}
else if (cur->_right == nullptr)//右为空
{
if (_root == cur)//相当于是更新根节点
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
cur = nullptr;
}
else
{
//找到右子树最小节点进行替换
Node* minParent = cur;
Node* min = cur->_right;
while (min->_left)
{
minParent = min;
min = min->_left;
}
swap(cur->_key, min->_key);
if (minParent->_left == min)
minParent->_left = min->_right;
else
minParent->_right = min->_right;
delete min;
}
return true;
}
}
return false;
}
void Inorder()
{
_Inorder(_root);
cout << endl;
}
//模拟实现搜索二叉树非递归方式
bool FindR(const K& key)
{
return _FindR(_root, key);
}
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
~BST()
{
_Destory(_root);
}
private:
Node* _Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* copyRoot = new Node(root->_key);
copyRoot->_left = _Copy(root->_left);
copyRoot->_right = _Copy(root->_right);
return copyRoot;
}
void _Destory(Node*& root)
{
if (root == nullptr)
{
return;
}
_Destory(root->_left);
_Destory(root->_right);
delete root;
root = nullptr;
}
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
return false;
if (root->_key < key)
return _EraseR(root->_right, key);
else if (root->_key > key)
return _EraseR(root->_left, key);
else
{
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
//找右树的最左节点替换删除
Node* min = root->_right;
while (min->_left)
{
min = min->_left;
}
swap(root->_key, min->_key);
return _EraseR(root->_right, key);
}
delete del;
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;
}
bool _FindR(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;
}
void _Inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_Inorder(root->_left);
cout << root->_key << " ";
_Inorder(root->_right);
}
private:
Node* _root = nullptr;
};
三、搜索二叉树的应用
1、K模型:K模型即只有key值作为关键码,结构中值需要存储key即可,关键码即为需要搜索到的值。
应用场景:门禁系统、车库系统、检查一篇文章单词拼写是否正确
2、KV模型:每一个关键码key,都有与之对应的值value,构建一颗搜索二叉树。
·比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对
·比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词出现次数就是<word, count>就构成一种键值对。