
二叉搜索树,又称搜索二叉树、二叉排序树,是一种具有特殊性质的二叉树:
由于它本身和它的子树都是二叉搜索树,不难想象:二叉搜索树的中序遍历一定是升序序列

从名字就能看出来,二叉搜索树主要用于搜索。最优情况下,如果它接近完全二叉树的结构,不难分析,最大搜索次数即其树的高度:log2N;最差情况下,如果它接近单支树的结构,则其搜索次数接近结点个数:N。

所以综合来看二叉搜索树的增删查改效率为O(N),这样的时间复杂度还是太高了。必须将二叉搜索树的结构进一步优化,也就是以后要学习的平衡二叉搜索树AVL树、红黑树。
首先需要定义出结点的结构:
template<class K>
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{ }
};由于树的类中涉及到结点资源的开辟和删除,所以需要我们自己写拷贝构造、赋值重载、析构函数等。
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
private:
Node* _root;
public:
//......
}//默认构造一棵空树
BSTree()
:_root(nullptr)
{ }之前我们学习链式结构的树,它的很多操作都需要用递归完成,但我们这里的拷贝构造、析构函数没办法传参递归怎么办?我们可以写出递归的函数后,“包装”起来:
BSTree(const BSTree<K>& t)
{
//实际完成拷贝功能的是Copy函数,我们的拷贝构造只是把他包装起来
_root = Copy(t._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;
}~BSTree()
{
Destory(_root);
_root = nullptr;
}
void Destory(Node* root)
{
if (root == nullptr)
{
return;
}
//利用递归完成所有结点的资源释放
Destory(root->_left);
Destory(root->_right);
delete root;
}拷贝构造函数的Copy函数和析构函数的Destory函数仅供它们内部使用,所以可以把它们放到private部分
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}二叉搜索树的插入要按照搜索树的性质寻找正确位置插入:

代码实现:
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
//parent保存待插入位置的父结点
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
//如果key值已存在,则插入失败
return false;
}
}
//cur找到了正确的位置
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 (key < cur->_key)
{
cur = cur->_left;
}
else if (key > cur->_key)
{
cur = cur->_right;
}
else //key == cur->_key,找到了
{
return true;
}
}
return false;
}二叉搜索树的删除相对复杂,首先查找待删除元素结点,不存在则返回false。 若存在,则显然有以下几种情形:(假设要删除的结点为x) x没有孩子、有左孩子或右孩子、有两个孩子。 为了删除结点后不破坏搜索二叉树的性质,它们要用不同的解决方法:



代码实现,可以将情形1和情形2整合讨论:
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else //找到了待删除结点cur,开始删除
{
//如果cur左孩子为空,就连接parent和cur的右子
if (cur->_left == nullptr)
{
//特殊情况cur为根结点,cur右孩子当根结点
if (cur == _root)
{
_root = cur->_right;
}
else
{
//如果cur是parent的左,则让parent的左指向cur的右子
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
//如果cur是parent的右,则让parent的右指向cur的右子
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
//如果cur右孩子为空,就连接parent和cur的左子
else if (cur->_right == nullptr)
{
//特殊情况cur为根结点,cur左孩子当根结点
if (cur == _root)
{
_root = cur->_left;
}
else
{
//如果cur是parent的左,则让parent的左指向cur的左子
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
//如果cur是parent的右,则让parent的右指向cur的左子
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
else //两个孩子,这里选择找左子树的最大结点替换
{
Node* LeftMaxParent = cur;
Node* LeftMax = cur->_left;
//找左子树的最右结点
while (LeftMax->_right)
{
LeftMaxParent = LeftMax;
LeftMax = LeftMax->_right;
}
swap(cur->_key, LeftMax->_key);
//此时LeftMax一定没有右孩子,可能有左孩子
//还可能LeftMaxParent就是根结点,所以要判断LeftMax是其父的左还是右孩子
if (LeftMaxParent->_left == LeftMax)
{
LeftMaxParent->_left = LeftMax->_left;
}
else
{
LeftMaxParent->_right = LeftMax->_left;
}
delete LeftMax;
}
return true;
}
}
return false;
}为了验证二叉搜索树的性质,我们要判断中序遍历的结果是否是升序的:
void InOrder()
{
_InOrder(_root);
cout << endl;
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}同样地,将_InOrder函数放在private中。
综上,二叉搜索树的完整实现:
namespace lydly
{
template<class K>
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{ }
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
private:
Node* _root;
public:
//默认构造一棵空树
BSTree()
:_root(nullptr)
{ }
//拷贝构造
BSTree(const BSTree<K>& t)
{
_root = Copy(t._root);
}
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}
~BSTree()
{
Destory(_root);
_root = nullptr;
}
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
//如果key值已存在,则插入失败
return false;
}
}
//cur找到了正确的位置
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 (key < cur->_key)
{
cur = cur->_left;
}
else if (key > cur->_key)
{
cur = cur->_right;
}
else //key == cur->_key找到了
{
return true;
}
}
return false;
}
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else //找到了待删除结点,开始删除
{
//如果cur左孩子为空,就连接parent和cur的右子
if (cur->_left == nullptr)
{
//特殊情况cur为根结点,cur右子当根
if (cur == _root)
{
_root = cur->_right;
}
else
{
//如果cur是parent的左,则让parent的左指向cur的右子
if (cur == parent->_left)
{
parent->_left = cur->_right;
}
//如果cur是parent的右,则让parent的右指向cur的右子
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
//如果cur右孩子为空,就连接parent和cur的左子
else if (cur->_right == nullptr)
{
//特殊情况cur为根结点,cur左子当根
if (cur == _root)
{
_root = cur->_left;
}
else
{
//如果cur是parent的左,则让parent的左指向cur的左子
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
//如果cur是parent的右,则让parent的右指向cur的左子
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
else //两个孩子,这里选择找左子树的最大结点替换
{
Node* LeftMaxParent = cur;
Node* LeftMax = cur->_left;
//找左子树的最右结点
while (LeftMax->_right)
{
LeftMaxParent = LeftMax;
LeftMax = LeftMax->_right;
}
swap(cur->_key, LeftMax->_key);
//此时LeftMax一定没有右孩子,可能有左孩子
//还可能LeftMaxParent就是根结点,所以要判断LeftMax是其父的左还是右孩子
if (LeftMaxParent->_left == LeftMax)
{
LeftMaxParent->_left = LeftMax->_left;
}
else
{
LeftMaxParent->_right = LeftMax->_left;
}
delete LeftMax;
}
return true;
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void Destory(Node* root)
{
if (root == nullptr)
{
return;
}
Destory(root->_left);
Destory(root->_right);
delete root;
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
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;
}
};
} 简单测试:

没有问题。
上面我们实现的二叉搜索树,结点中只存储了一个key,称为关键码,关键码即为需要搜索的值,使用这种二叉搜索树的场景只需要判断key是否存在。key的搜索场景实现的二叉搜索树支持增删查,不支持修改,因为修改key值破坏二叉搜索树性质了。
使用这种key二叉搜索树的场景,比如小区车库,只有业主的车能进入,车牌号作为key值,就在系统中检查key是否存在以确定是否为业主的车。
还有一种key/value的搜索场景:每一个关键码key,都有与之对应的value,value可以为任意类型对象,增删查还是以key为关键码在二叉搜索树中操作,修改不能改key,但是可以修改value。
使用这种key/value二叉搜索树的场景,比如英语词典,树的结构中存储key(英文)和value(中文),搜索时输入英文作为key进行查找,找到后输出对应的value中文。
key/value二叉搜索树的实现:
namespace lydly
{
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;
private:
Node* _root = nullptr;
public:
BSTree()
{ }
BSTree(const BSTree<K, V>& t)
{
_root = Copy(t._root);
}
BSTree<K, V>& operator=(BSTree<K, V> t)
{
swap(_root, t._root);
return *this;
}
~BSTree()
{
Destory(_root);
_root = nullptr;
}
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->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key, value);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
Node* 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 cur;
}
}
return nullptr;
}
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
{
// 准备删除
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* pMinRight = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
pMinRight = minRight;
minRight = minRight->_left;
}
swap(cur->_key, minRight->_key);
if (pMinRight->_left == minRight)
{
pMinRight->_left = minRight->_right;
}
else
{
pMinRight->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << ":" << root->_value << endl;
_InOrder(root->_right);
}
void Destory(Node* root)
{
if (root == nullptr)
{
return;
}
Destory(root->_left);
Destory(root->_right);
delete root;
}
Node* Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* copy = new Node(root->_key, root->_value);
copy->_left = Copy(root->_left);
copy->_right = Copy(root->_right);
return copy;
}
};
}本篇完,感谢阅读。