文章目录
- 1. 二叉搜索树的概念
- 2. 二叉搜索树的代码实现
- 2.1 Find ( ) 查找的实现
- 2.2 Insert () 插入的实现
- 2.3 InOrder ( ) 中序遍历的实现
- 2.4 Erase ( ) 删除的实现
- 3. 二叉搜索树的应用
1. 二叉搜索树的概念
🐧① 二叉搜索树(BST,Binary Search Tree),也称二叉排序树或者二叉查找树;
🍎② 左子树的值都 小于 根结点,右子树的值都大于
根结点;
✌③ 一颗搜索树如下所示:
2. 二叉搜索树的代码实现
2.1 Find ( ) 查找的实现
🐧① 从根结点开始查找🔍,key 比根结点大则往根结点右边走,比根结点小则往根结点的左边走;
🐧② 最多查找高度次
// 二叉树的查找
bool Find(const K& key)
{
if (_root == nullptr)
{
return false;
}
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
return true;
}
}
return false;
}
2.2 Insert () 插入的实现
🐧① 树为空,直接在根结点插入数据;
🐧② 树不为空,则先查找要插入结点的位置,然后再插入数据;
// 插入的实现
bool Insert(const K& key)
{
// 1.可能刚开始的时候是一颗 空树 的情况
if (_root == nullptr)
{
// 这是 new 的特性:new对自定义类型自动调用构造函数并完成初始化
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
// 二叉搜索树一般不允许数据冗余的情况
return false;
}
}
cur = new Node(key);
if (parent->_key > key)
{
parent->_left = cur;
}
else if (parent->_key < key)
{
parent->_right = cur;
}
return true;
}
2.3 InOrder ( ) 中序遍历的实现
🐧① 因为中序遍历要用到 递归
实现,因为类外面的测试函数要用到 BSTree
这个类的 _root
私有的成员变量,在类外面无法获取,所以我们该怎么获取呢?
a. 将其设置为友元函数(不可取,因为这只是一个测试函数,不能随意的将测试函数作为友元);
b. 采取缺省函数,给默认值(不可取);
c.将递归函数嵌套定义(可取)
2.4 Erase ( ) 删除的实现
二叉搜索树删除的 细节比较多,可大致分成以下三种情况;
🐧① 要删除的结点只有左孩子结点
🐧② 如果要删除的结点有左右孩子结点
采取替换法,用待删除结点的左子树最大结点或者右子树的最小结点来替换待删除的结点;
下面这两张图极其重要,可以完美的解释这段代码:
// 二叉搜索树的删除
bool Erase(const K& key)
{
if (_root == nullptr)
return false;
Node* cur = _root;
Node* parent = cur;
// 1. 首先找到要删除的结点
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
// ① 如果要删除的结点没有 右孩子
if (cur->_right == nullptr)
{
// 如果要删除的结点是根结点
if (parent == cur)
{
_root = cur->_left;
}
else
{
if (parent->_key > key)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
// ② 如果要删除的结点没有 左孩子
else if (cur->_left == nullptr)
{
// 如果要删除的结点是根结点
if (parent == cur)
{
_root = cur->_right;
}
else
{
if (parent->_key > key)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
// ③ 如果要删除的结点有 左右孩子结点
else
{
// 先找到右子树的最小结点
Node* rightMin = cur->_right;
Node* rightMinParent = cur;
while (rightMin->_left)
{
rightMinParent = rightMin;
rightMin = rightMin->_left;
}
//cur = new Node(key);
//rightMinParent->_right = rightMin->_right;
//if (rightMinParent->_key > rightMin->_key)
//{
// rightMinParent->_left = rightMin->_right;
//}
//else
//{
// rightMinParent->_right = rightMin->_right;
//}
//swap(rightMin->_key, cur->_key);
swap(cur->_key, rightMin->_key);
// 判断 rightMin 在 rightMinParent的左边还是右边
if (rightMinParent->_left == rightMin)
rightMinParent->_left = rightMin->_right;
else
rightMinParent->_right = rightMin->_right;
delete rightMin;
}
return true;
}
}
return false;
}
3. 二叉搜索树的应用
🐧① K模型:K模型即只有key作为关键码
,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
在二叉搜索树中检索该单词是否存在
,存在则拼写正确,不存在则拼写错误。
🐧②KV模型:每一个关键码 key,都有与之对应的值 Value 即<Key, Value>的键值对。
统计单词次数,统计成功后,
给定单词就可快速找到其出现的次数
,单词与其出现次数就是<word, count>就构成一种键值对;
- 以下是改造成 KV 模型的代码:
namespace key_value {
template<class K, class V>
struct BSTreeNode {
BSTreeNode<K, V>* _left;
BSTreeNode<K, V>* _right;
K _key;
V _value;
// 初始化
BSTreeNode(const K& key, const V& value)
:_left(nullptr)
, _right(nullptr)
, _key(key)
, _value(value)
{}
};
template<class K, class V>
class BSTree {
typedef BSTreeNode<K, V> Node;
public:
// 插入
bool Insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key, value);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key, value);
if (parent->_key > key)
{
parent->_left = cur;
}
else if (parent->_key < key)
{
parent->_right = cur;
}
return true;
}
// 查找
Node* Find(const K& key)
{
if (_root == nullptr)
return nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
return cur;
}
}
return nullptr;
}
// 删除
bool Erase(const K& key)
{
// 1.先找到要删除的结点
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
// 如果要删除的结点左孩子为空
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;
}
// 如果要删除的结点右孩子为空
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
// 左右孩子都不为空的情况
// 替换法:寻找右子树的最小结点来替换
else
{
Node* rightMinParent = cur;
Node* rightMin = cur->_right;
// 找到右子树的最小结点
while (rightMin->_left)
{
rightMinParent = rightMin;
rightMin = rightMin->_left;
}
swap(cur->_key, rightMin->_key);
// 最左节点不一定是叶子节点
if (rightMin == rightMinParent->_right)
{
rightMinParent->_right = rightMin->_right;
}
else
{
rightMinParent->_left = rightMin->_right;
}
delete rightMin;
}
return true;
}
}
return false;
}
// 中序遍历
void Inorder()
{
_Inorder(_root);
}
private:
void _Inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_Inorder(root->_left);
cout << root->_key << " " << root->_value;
_Inorder(root->_right);
}
Node* _root = nullptr;
};
// 查找是否有某种水果
void TestBSTreeKeyValue()
{
BSTree<string, string> tr2;
tr2.Insert("apple", "苹果");
tr2.Insert("orange", "橘子");
tr2.Insert("pear", "梨");
tr2.Insert("strawberry", "草莓");
string str;
while (cin >> str)
{
BSTreeNode<string, string>* ptr = tr2.Find(str);
if (ptr != nullptr)
{
cout << ptr->_key << " " << ptr->_value << endl;
}
else
{
cout << "Sorry,查无此水果,请重新输入!" << endl;
}
}
}
// 统计某种水果的数量
void TestBSTreeCount()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };
BSTree<string, int> countTree;
for (auto str : arr)
{
BSTreeNode<string, int>* ptr = countTree.Find(str);
if (ptr == nullptr)
{
countTree.Insert(str, 1);
}
else
{
ptr->_value++;
}
}
countTree.Inorder();
}
}