一、二叉搜索树的概念
二叉搜索树又称二叉排序树,它具有一下性质:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值;
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值;
- 它的左右子树也分别为二叉搜索树。

注意:空树可以是任意类型的树,所以空树也是一颗二叉搜索树。
二、二叉搜索树的优点
- 最优情况下,二叉搜索树为完全二叉树 (或者接近完全二叉树),其平均比较次数为 O(logN)。
- 最差情况下,二叉搜索树退化为单支树( 或者类似单支),其平均比较次数为 O(N)。
- 所以,二叉搜索树进行查找的时间复杂度为 O(N)。

可能有的同学会想,既然二叉搜索树查找的时间复杂度为 O(N),那我们为什么不直接用二分查找呢?毕竟二分查找的时间复杂度可是 O(logN),这是因为二分查找存在许多限制:
- 二分查找要求数据必须有序;
- 二分查找使用顺序表进行数据存储为了保持有序,插入、删除数据效率低,而在实际开发中,我们是要经常插入删除数据的;
而且,只有当数据有序或接近有序时二叉搜索树查找数据的时间复杂度才为 O(N),大部分情况下查找效率都是要远高于 O(N) 的;同时,在实际开发中我们用的并不是单纯的二叉搜索树树,而是它的改进版 -- 二叉搜索平衡树 (AVL树) ,即插入数据后对二叉搜索树进行调整,让其左右子树尽量变得平衡,这样,二叉搜索树的查找效率就几乎等于 O(logN) 了。

如上图就是搜索二叉树的模型,根8左边子树上的数都比8小(左子树性质),右边子树上的数都比8大(右子树性质)。并且这种性质在3为根的左子树,6为根的左子树等等都成立(递归性质)。
如右图,搜索二叉树除了左边这种每一个键值不重复的情况,还有右边这颗树一样的重复键值情况。这个我们会在接下来讲到:
注:关于二叉搜索平衡树我们将在下一节介绍。
2.2、关于相等值的处理
二叉搜索树中是否支持插入相等的值,取决于具体的实现和使用场景:
- 1.不允许重复:某些实现(如 C++ STL 中的 set 和 map)不允许插入与已有节点值相等的节点。
- 2.允许重复:另一些实现(如 C++ STL 中的 multiset 和 multimap)则支持插入相等的值,这些值通常会被放置在同一子树(通常是右子树)上。
set,map,multiset 和 multimap这些STL我们会在接下来的文章中详细介绍。他们的底层是平衡二叉树中的红黑树。
三、二叉搜索树的操作及实现
我们下面讲的二叉搜索树的操作均以数组 a 中的数据为例:
cpp
int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

3.1、实现框架结构
cpp
//节点类
template <class K>
class BSTNode
{
public:
BSTNode(const K& key = K())//const不改值,引用减少拷贝,无参的时候K()默认调用构造
:_key(key)
, _left(nullptr))
, _right = (nullptr)
{
}
~BSTNode();
K _key;
BSTNode<K>* _left;
BSTNode<K>* _right;
};
//二叉搜索树类
template <class K>
class BSTree
{
typedef BSTNode<K> Node;
public:
BSTree()
:_root(nullptr)
{
}
~BSTree();
private:
Node* _root;
};
- 二叉搜索树节点模板类BSTnode
类模板参数 K 表示节点中存储的数据类型,使该节点结构可以适用于任意类型的数据。
节点内部包含三个成员:_key 用于存储节点的关键字(数据),left 和 right 分别指向当前节点的左子节点和右子节点。
构造函数 BSTnode(const K& key=K()) 接收一个键值 key,用于初始化节点的数据成员 _key,同时将左右孩子指针 left 和 right 初始化为空,表示新创建的节点在初始状态下没有子节点。
- 二叉搜索树模板类BSTnode
typedef一下BSTNode Node
3.2、二叉搜索树的查找
从根节点开始比较,查找值 x:
- 若 x 大于根节点的值,则向右子树查找;
- 若 x 小于根节点的值,则向左子树查找。
- 最多查找树的高度次,若遍历到空节点仍未找到目标值,则说明该值不存在。

cpp
//查找
Node* Find(K key)
{
//从根节点开始找
Node* cur = _root;
//只要不为空就一直找
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
cur = cur->_right;
}
//这里我们相等就直接返回那个值
else
{
return cur;
}
}
//没找到
return nullptr;
}
3.3、二叉搜索树的插入
插入的具体过程如下:
- 树为空,则直接新增结点,赋值给root指针。
- 树不空,按二叉搜索树性质,插入值比当前结点大往右走,插入值比当前结点小往左走,找到空位置,插入新结点。
- 如果支持插入相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,找到空位置,插入新结点。(要注意的是要保持逻辑一致性,插入相等的值不要一会往右走,一会往左走)
我们用一个例子解释一下原理:
如上有一颗二叉搜索树,我们这个时候要插入一个数16。如下图:按照二叉树的性质不断向下查找到指定位置并插入。

如下图,当我们要插入一个重复的数字的时候,可以规定他在原有的重复数字的左子树或者是右子树。 然后用二叉树的规则同样进行查找并插入。
cpp
//插入
bool Insert(const K& key)
{
Node* cur = _root;
//只要不为空就一直找
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
cur = cur->_right;
}
//这里我们相等就设计成不需插入了
else
{
return false;
}
}
}
我们写到这里发现一个事此时cur为空了没意义我们要找到他的前一个节点在进行比较然后再他的左边或者右边插入
cpp
//插入
bool Insert(const K& key)
{
//先创建一个节点等等插入
Node* newnode = new Node(key);
//空树直接作为根节点
if (_root == nullptr)
{
_root = newnode;
return true;
}
Node* cur = _root;
//记录上一个节点
Node* parent = nullptr;
//只要不为空就一直找
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
//这里我们相等就设计成不需插入了
else
{
return false;
}
}
//此时parent就是插入节点的父节点了
if (parent->_key > key)
parent->left = newnode;
else if (parent->_key < key)
parent->right = newnode;
return true;
}
3.4、二叉搜索树的删除
3.4.1、执行删除操作时共有多少种情况
首先查找元素是否在二叉搜索树中,如果不存在,则返回 false。如果查找元素存在,则分以下四种情况分别处理:(假设要删除的结点为 N)
- 情况1. 要删除结点 N 左右孩子均为空
- 情况2. 要删除的结点 N 左孩子为空,右孩子结点不为空
- 情况3. 要删除的结点 N 右孩子为空,左孩子结点不为空
- 情况4. 要删除的结点 N 左右孩子结点均不为空
3.4.2直接删除法
把 N 结点的父亲对应孩子指针指向空,直接删除 N 结点(情况 1 可以当成 2 或者 3 处理,效果是一样的)
3.4.2托孤法
情况二,把 N 结点的父亲对应孩子指针指向 N 的右孩子,直接删除 N 结点。
情况三,把 N 结点的父亲对应孩子指针指向 N 的左孩子,直接删除 N 结点。
如图所示,当前需要删除的结点为 14。该结点只有一个孩子 13,按照托孤法的处理方式,只需让 14 的父结点 10 的右指针直接指向 13,再删除结点 14 即可。
这样既完成了结点删除,又保证了二叉搜索树"左小右大"的结构性质不被破坏。如果 13 结点下方还存在子树,也会随着 13 一同接入原来的位置。

4.3.3替换删除法(最复杂)
先看图,假设我们要删除3结点或者是8结点这类有两个子节点的结点。
无法直接删除 3 结点,因为 3 的两个孩子无处安放,(为什么?如果还是用托孤法,被删除结点有两个孩子,被删除结点的父亲只有一个空出来的指针,指向其中一个孩子另外一个孩子怎么办?)所以删除有两个孩子的结点,我们必须另寻他法。
回想我们曾今在堆删除堆顶元素的时候采用的方法:从堆的底部取一个数和堆顶元素交换,然后删除堆底元素并对对顶做向下调整算法。
我们也可不可以将一个数和被删除的数交换来实现替换式的删除?因为搜索二叉树的规则严谨性,这个数不能随便取得。于是我们设计出以下规则来取得这个替换数。
找 3 左子树的值最大结点 R(最右结点)或者 3 右子树的值最小结点 R(最左结点)替代 3,因为这两个结点中任意一个,放到 3 的位置,都满足二叉搜索树的规则。
(替代 3 的意思就是 3 和 4 的两个结点的值交换,转而变成删除 3(和4交换后) 结点,4 结点符合情况 2 或情况 3,可以直接删除。)
如上图,假设我们要去删除3,并且找到了右子树的最小结点4,这个时候交换4和3:如下图:
如果在这个时候继续选择从头遍历去查找3并删除的话,我们是找不到的,因为3的位置不符合二叉搜索树的规则。就是这个细节我们下面的代码要注意一下。
cpp
bool Erase(const K& key)
{
//当前节点
Node* cur = _root;
//父节点
Node* parent = nullptr;
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
//这里我们相等就找到了
else
{
//左右孩子都为空
if (cur->_left == nullptr && cur->_right == nullptr)
{
delete(cur);
}
//左孩子为空
else if (cur->_left == nullptr && cur->_right != nullptr)
{
// 2
// null 3
//先判断这个防止空指针
if (cur = _root)
{
_root = cur->_right;
delete(cur)
}
if (parent->_key > cur->_key)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete(cur);
}
//右孩子为空
else if (cur->_left != nullptr && cur->_right == nullptr)
{
// 2
// null 3
//先判断这个防止空指针
if (cur = _root)
{
_root = cur->_right;
delete(cur)
}
if (parent->_key > cur->_key)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete(cur);
}
// 左右孩子都不为空
else
{
//找右树的最小节点,即最左节点
Node* minRight = cur->_right;
Node* minParent = cur;
//最左节点只是没有左孩子,还是可能有右孩子,所以这里需要记录父节点,方便后面继续托孤
//同时,这里parent不能初始化为空,因为cur->_right可能直接就为右子树的最左节点,此时不会进循环,parent也不会被赋值(空指针了)
while (minRight->_left)
{
minParent = minRight;
minRight = minRight->_left;
}
//把最小节点的右孩子托孤给最小的父亲
if (minParent->_left == minRight)
minParent->_left = minRight->_right;
else
minParent->_right = minRight->_right;
//替换minRight和cur节点的值(直接将minRight值赋给cur就行,因为minRight最终会被释放掉)
cur->_key = minRight->_key;
delete(minRight);
}
}
}
//空树和出来的不能删除
return false;
}
3.5、交换函数
交换根节点就好了其他节点都是挂在根节点下的
cpp
//交换函数
void swap(BSTree& BS1)
{
std::swap(_root, BS1._root);
}
3.6、中序遍历
cpp
//子函数
void _inorder(Node* root)
{
if (root == nullptr)
return;
_inorder(root->_left);
cout << root->_key << "->";
_inorder(root->_right);
}
//中序遍历
void inorder()
{
_inorder(_root);
cout << NULL << endl;
}
外面拿不到私有的root无法传值所以我们封装了一个子函数
3.7、析构
cpp
~BSTree()
{
destory(_root);
_root = nullptr;
}
//删除节点
void destory(Node* root)
{
if (root == nullptr)
{
return;
}
destory(root->_left);
destory(root->_right);
delete(root);
}
3.8、拷贝构造
cpp
//拷贝构造
BSTree(const BSTree& tree)
{
_root = Copy(tree._root);
}
Node* Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
//前序拷贝
Node* newnode = new Node(root->_key);
newnode->left = Copy(root->left);
newnode->right = Copy(root->right);
return newnode;
}
3.9、赋值重载
cpp
//赋值
BSTree& operator= (const BSTree tree)
{
swap(tree);
return *this;
}
3.10、全部代码
cpp
#include <iostream>
using namespace std;
namespace ljm
{
//节点类
template <class K>
class BSTNode
{
public:
BSTNode(const K& key = K())//const不改值,引用减少拷贝,无参的时候K()默认调用构造
:_key(key)
, _left(nullptr)
, _right (nullptr)
{
}
K _key;
BSTNode<K>* _left;
BSTNode<K>* _right;
};
//二叉搜索树类
template <class K>
class BSTree
{
typedef BSTNode<K> Node;
public:
BSTree()
:_root(nullptr)
{
}
~BSTree()
{
destory(_root);
_root = nullptr;
}
//删除节点
void destory(Node* root)
{
if (root == nullptr)
{
return;
}
destory(root->_left);
destory(root->_right);
delete(root);
}
//赋值
BSTree& operator= (const BSTree tree)
{
swap(tree);
return *this;
}
//拷贝构造
BSTree(const BSTree& tree)
{
_root = Copy(tree._root);
}
Node* Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
//前序拷贝
Node* newnode = new Node(root->_key);
newnode->left = Copy(root->left);
newnode->right = Copy(root->right);
return newnode;
}
//查找
Node* Find(K key)
{
//从根节点开始找
Node* cur = _root;
//只要不为空就一直找
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
cur = cur->_right;
}
//这里我们相等就直接返回那个值
else
{
return cur;
}
}
//没找到
return nullptr;
}
//插入
bool Insert(const K& key)
{
//先创建一个节点等等插入
Node* newnode = new Node(key);
//空树直接作为根节点
if (_root == nullptr)
{
_root = newnode;
return true;
}
Node* cur = _root;
//记录上一个节点
Node* parent = nullptr;
//只要不为空就一直找
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
//这里我们相等就设计成不需插入了
else
{
return false;
}
}
//此时parent就是插入节点的父节点了
if (parent->_key > key)
parent->_left = newnode;
else if (parent->_key < key)
parent->_right = newnode;
return true;
}
bool Erase(const K& key)
{
//当前节点
Node* cur = _root;
//父节点
Node* parent = nullptr;
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
//这里我们相等就找到了
else
{
//左右孩子都为空
if (cur->_left == nullptr && cur->_right == nullptr)
{
delete(cur);
}
//左孩子为空
else if (cur->_left == nullptr && cur->_right != nullptr)
{
// 2
// null 3
//先判断这个防止空指针
if (cur = _root)
{
_root = cur->_right;
delete(cur);
}
if (parent->_key > cur->_key)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete(cur);
}
//右孩子为空
else if (cur->_left = nullptr && cur->_right == nullptr)
{
// 2
// null 3
//先判断这个防止空指针
if (cur = _root)
{
_root = cur->_right;
delete(cur);
}
if (parent->_key > cur->_key)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete(cur);
}
// 左右孩子都不为空
else
{
//找右树的最小节点,即最左节点
Node* minRight = cur->_right;
Node* minParent = cur;
//最左节点只是没有左孩子,还是可能有右孩子,所以这里需要记录父节点,方便后面继续托孤
//同时,这里parent不能初始化为空,因为cur->_right可能直接就为右子树的最左节点,此时不会进循环,parent也不会被赋值(空指针了)
while (minRight->_left)
{
minParent = minRight;
minRight = minRight->_left;
}
//把最小节点的右孩子托孤给最小的父亲
if (minParent->_left == minRight)
minParent->_left = minRight->_right;
else
minParent->_right = minRight->_right;
//替换minRight和cur节点的值(直接将minRight值赋给cur就行,因为minRight最终会被释放掉)
cur->_key = minRight->_key;
delete(minRight);
}
}
}
//空树和出来的不能删除
return false;
}
//交换函数
void swap(BSTree& BS1)
{
std::swap(_root, BS1._root);
}
//子函数
void _inorder(Node* root)
{
if (root == nullptr)
return;
_inorder(root->_left);
cout << root->_key << "->";
_inorder(root->_right);
}
//中序遍历
void inorder()
{
_inorder(_root);
cout << NULL << endl;
}
private:
Node* _root;
};
void test1()
{
BSTree<int> BS1;
BS1.Insert(3);
BS1.Insert(1);
BS1.Insert(9);
BS1.Insert(5);
BS1.Insert(4);
BS1.Insert(10);
BS1.Insert(20);
BS1.inorder();
}
}

四、key搜索场景/key-value搜索场景
4.1、 key搜索场景
只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断key在不在。key的搜索场景实现的二叉树搜索树支持增删查,但是不支持修改,修改key破坏搜索树结构了。
场景1:小区无人值守车库,小区车库买了车位的业主车才能进小区,那么物业会把买了车位的业主的车牌号录入后台系统,车辆进入时扫描车牌在不在系统中,在则抬杆,不在则提示非本小区车辆,无法进入。
场景2:检查一篇英文文章单词拼写是否正确,将词库中所有单词放入二叉搜索树,读取文章中的单词,查找是否在二叉搜索树中,不在则波浪线标红提示。
4.2、key/value搜索场景
每一个关键码key,都有与之对应的值value,value可以任意类型对象。树的结构中(结点)除了需要存储key还要存储对应的value,增/删/查还是以key为关键字走二叉搜索树的规则进行比较,可以快速查找到key对应的value。key/value的搜索场景实现的二叉树搜索树支持修改,但是不支持修改key,修改key破坏搜索树性质了,可以修改value。
场景1:简单中英互译字典,树的结构中(结点)存储key(英文)和vlauе(中文),搜索时输入英文,则同时查找到了英文对应的中文。
场景2:商场无人值守车库,入口进场时扫描车牌,记录车牌和入场时间,出口离场时,扫描车牌,查找入场时间,用当前时间-入场时间计算出停车时长,计算出停车费用, 缴费后抬杆,车辆离场。
4.3、 key/value搜索场景代码
我之前带着大家写的是key搜索场景的代码,key/value搜索场景的代码只需要在次基础上稍作修改即可,以下呈现给大家。
cpp
#include <iostream>
using namespace std;
namespace ljm
{
//节点类
template <class K, class V>
class BSTNode
{
public:
BSTNode(const K& key = K(), const V& val = V())//const不改值,引用减少拷贝,无参的时候K()默认调用构造
:_key(key)
, _val(val)
, _left(nullptr)
, _right (nullptr)
{
}
K _key;
V _val;
BSTNode<K,V>* _left;
BSTNode<K,V>* _right;
};
//二叉搜索树类
template <class K ,class V>
class BSTree
{
typedef BSTNode<K,V> Node;
public:
BSTree()
:_root(nullptr)
{
}
~BSTree()
{
destory(_root);
_root = nullptr;
}
//删除节点
void destory(Node* root)
{
if (root == nullptr)
{
return;
}
destory(root->_left);
destory(root->_right);
delete(root);
}
//赋值
BSTree& operator= (const BSTree tree)
{
swap(tree);
return *this;
}
//拷贝构造
BSTree(const BSTree& tree)
{
_root = Copy(tree._root);
}
Node* Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
//前序拷贝
Node* newnode = new Node(root->_key, root->_value);
newnode->left = Copy(root->left);
newnode->right = Copy(root->right);
return newnode;
}
//查找
Node* Find(K key)
{
//从根节点开始找
Node* cur = _root;
//只要不为空就一直找
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
cur = cur->_right;
}
//这里我们相等就直接返回那个值
else
{
return cur;
}
}
//没找到
return nullptr;
}
//插入
bool Insert(const K& key, const V& val)
{
//先创建一个节点等等插入
Node* newnode = new Node(key,val);
//空树直接作为根节点
if (_root == nullptr)
{
_root = newnode;
return true;
}
Node* cur = _root;
//记录上一个节点
Node* parent = nullptr;
//只要不为空就一直找
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
//这里我们相等就设计成不需插入了
else
{
return false;
}
}
//此时parent就是插入节点的父节点了
if (parent->_key > key)
parent->_left = newnode;
else if (parent->_key < key)
parent->_right = newnode;
return true;
}
bool Erase(const K& key)
{
//当前节点
Node* cur = _root;
//父节点
Node* parent = nullptr;
while (cur)
{
//比他小去左边找
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
//比他大去右边找
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
//这里我们相等就找到了
else
{
//左右孩子都为空
if (cur->_left == nullptr && cur->_right == nullptr)
{
delete(cur);
}
//左孩子为空
else if (cur->_left == nullptr && cur->_right != nullptr)
{
// 2
// null 3
//先判断这个防止空指针
if (cur = _root)
{
_root = cur->_right;
delete(cur);
}
if (parent->_key > cur->_key)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete(cur);
}
//右孩子为空
else if (cur->_left = nullptr && cur->_right == nullptr)
{
// 2
// null 3
//先判断这个防止空指针
if (cur = _root)
{
_root = cur->_right;
delete(cur);
}
if (parent->_key > cur->_key)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete(cur);
}
// 左右孩子都不为空
else
{
//找右树的最小节点,即最左节点
Node* minRight = cur->_right;
Node* minParent = cur;
//最左节点只是没有左孩子,还是可能有右孩子,所以这里需要记录父节点,方便后面继续托孤
//同时,这里parent不能初始化为空,因为cur->_right可能直接就为右子树的最左节点,此时不会进循环,parent也不会被赋值(空指针了)
while (minRight->_left)
{
minParent = minRight;
minRight = minRight->_left;
}
//把最小节点的右孩子托孤给最小的父亲
if (minParent->_left == minRight)
minParent->_left = minRight->_right;
else
minParent->_right = minRight->_right;
//替换minRight和cur节点的值(直接将minRight值赋给cur就行,因为minRight最终会被释放掉)
cur->_key = minRight->_key;
delete(minRight);
}
}
}
//空树和出来的不能删除
return false;
}
//交换函数
void swap(BSTree& BS1)
{
std::swap(_root, BS1._root);
}
//子函数
void _inorder(Node* root)
{
if (root == nullptr)
return;
_inorder(root->_left);
cout << root->_key << "->"<<root->_val<<" ";
_inorder(root->_right);
}
//中序遍历
void inorder()
{
_inorder(_root);
cout << (void*)NULL << endl;
}
private:
Node* _root;
};
void test1()
{
BSTree<int,int> BS1;
BS1.Insert(3,6);
BS1.Insert(1,4);
BS1.Insert(9,10);
BS1.Insert(5,50);
BS1.Insert(4,10);
BS1.Insert(10,5);
BS1.Insert(20,3);
BS1.inorder();
}
}

下面我们尝试实现音译汉和统计每种类型水果数量的功能
cpp
void BSTree_KV_test1()
{
//英译汉
BSTree<string, string> dict;
//输入<key,value>对,形成词库
dict.Insert("BinarryTree", "二叉树");
dict.Insert("SearchTree", "搜索树");
dict.Insert("English", "英语");
dict.Insert("Chinese", "汉语");
dict.Insert("Blog", "博客");
//从词库中查找单词
string str;
while (cin >> str)
{
BSTNode<string, string>* ret = dict.Find(str);
if (ret == nullptr)
cout << "词库中不存在该单词" << endl;
else {
cout << ret->_val << endl;
}
}
}

cpp
void BSTree_KV_test2()
{
BSTree<string, int> BS1;
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };
for (string k: arr)
{
BSTNode<string,int>* ret = BS1.Find(k);
if (ret)
ret->_val++;
else
//第一次插入为1
BS1.Insert(k, 1);
}
BS1.inorder();
}
}
