二叉搜索树的概念
二叉搜索树,或者是一棵空树,或者是具有下列性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值均小于它的根节点的值;
- 若它的右子树不为空,则右子树上所有节点的值均大于它的根节点的值;
- 它的左、右子树也分别为二叉搜索树。
二叉搜索树的相关操作
- 二叉搜索树的节点定义
template<class T>
struct BSTNode
{
BSTNode(const T& data = T())
: _left(nullptr)
, _right(nullptr)
, _data(data)
{}
BSTNode<T>* _left;
BSTNode<T>* _right;
T _data;
};
- 二叉搜索树的结构
template <class T>
class BSTree
{
public:
typedef BSTNode<T> Node;
typedef Node* pNode;
BSTree(); //构造函数
BSTree(const BSTree<T>& bst); //拷贝构造函数
pNode Copy(pNode root);//拷贝根为root的树
void Destroy(pNode root);//销毁根为root的树
BSTree& operator == (const BSTree<T>& bst); //赋值运算符重载
~BSTree(); //析构函数
pNode Find(const T& data); //搜索,返回值为data的节点的地址
bool Insert(const T& data); //插入值为data的节点
bool Erase(const T& data); //删除值为data的节点
void _Inorder(pNode root); //中序遍历打印节点的值
void Inorder(); //中序遍历打印节点的值
private:
pNode _root = nullptr;
}
1. 查找
若根节点不为空:
- 要查找的 data == 根节点的 data,返回 true;
- 要查找的 data > 根节点的 data,在其右子树查找;
- 要查找的 data < 根节点的 data,在其左子树查找。
否则返回false。
template<class T>
typename BSTree<T>::pNode BSTree<T>::Find(const T& data)
{
if (_root == nullptr)
return nullptr;
pNode cur = _root;
while (cur)
{
if (data > cur->_data)
cur = cur->_right;
else if (data < cur->_data)
cur = cur->_left;
else
return cur;
}
return nullptr;
}
2. 插入
- 树为空,则直接插入
- 树不为空,按二叉搜索树的性质查找合适的插入位置,插入新节点
template<class T>
bool BSTree<T>::Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
return true;
}
pNode cur = _root;
pNode parent = nullptr;
while (cur)
{
parent = cur;
if (data > cur->_data)
cur = cur->_right;
else if (data < cur->_data)
cur = cur->_left;
else
return false;
}
cur = new Node(data);
if (data > parent->_data)
parent->_right = cur;
else
parent->_left = cur;
return true;
}
3. 删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回;
否则要删除的节点可能分下面四种情况:
- a. 要删除的节点无孩子节点
- b. 要删除的节点只有左孩子节点
- c. 要删除的节点只有右孩子节点
- d. 要删除的节点有左、右孩子节点
看起来有待删除节点有4中情况,实际情况 a 可以与情况 b 或者 c 合并起来,因此真正的删除过程如下:
- 情况b:删除该节点且使被删除节点的双亲结点指向被删除节点的左孩子结点
- 情况c:删除该节点且使被删除节点的双亲结点指向被删除结点的右孩子结点
- 情况d:找其左子树中的最大节点,即左子树中最右侧的节点,或者在其右子树中最小的节点,即右子树中最小的节点,替代节点找到后,将替代节点中的值交给待删除节点,转换成删除替代节点
template<class T>
bool BSTree<T>::Erase(const T& data)
{
if (_root == nullptr)
return false;
pNode cur = _root;
pNode parent = nullptr;
while (cur)
{
if (data == cur->_data)
break;
parent = cur;
if (data > cur->_data)
cur = cur->_right;
else if (data < cur->_data)
cur = cur->_left;
}
if (cur == nullptr)
return false;
if (cur->_left == nullptr && cur->_right == nullptr)
{
if (cur != _root)
{
if (cur == parent->_left)
parent->_left = nullptr;
else
parent->_right = nullptr;
}
else
_root = nullptr;
delete cur;
cur = nullptr;
}
else if (cur->_left == nullptr)
{
if (cur != _root)
{
if (cur == parent->_left)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
else
_root = cur->_right;
delete cur;
cur = nullptr;
}
else if (cur->_right == nullptr)
{
if (cur != _root)
{
if (cur == parent->_left)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
}
else
_root = cur->_left;
delete cur;
cur = nullptr;
}
else
{
pNode next = cur->_left;
parent = cur;
while (next->_right)
{
parent = next;
next = next->_right;
}
cur->_data = next->_data;
if (next == parent->_right)
parent->_right = next->_left;
else
parent->_left = next->_left;
delete next;
next = nullptr;
}
return true;
}
4.其他函数的实现
- 构造函数
template<class T>
BSTree<T>::BSTree()
:_root(nullptr)
{}
- 拷贝构造函数
template<class T>
BSTree<T>::BSTree(const BSTree<T>& bst)
{
_root = Copy(bst._root);
}
- 拷贝根为root的树
template<class T>
typename BSTree<T>::pNode BSTree<T>::Copy(BSTree<T>::pNode root)
{
if (root == nullptr)
return nullptr;
pNode newroot = new Node(root->_data);
newroot->_left = Copy(root->_left);
newroot->_right = Copy(root->_right);
return newroot;
}
- 销毁根为root的树
template<class T>
void BSTree<T>::Destroy(pNode root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
- 赋值运算符重载
template<class T>
BSTree<T>& BSTree<T>::operator==(const BSTree<T>& bst)
{
if (this != bst)
{
if (_root)
Destory(_root);
_root = Copy(bst._root);
}
return *this;
}
- 析构函数
template <class T>
BSTree<T>::~BSTree()
{
Destroy(_root);
}
- 中序遍历打印节点的值
template<class T>
void BSTree<T>::_Inorder(pNode root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_data << " ";
_Inorder(root->_right);
}
template<class T>
void BSTree<T>::Inorder()
{
_Inorder(_root);
cout << endl;
}
二叉搜索树的性能分析
-
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
-
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是节点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
-
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树
-
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:logN
-
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2