二叉搜索树
概念:
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值;
2.若它的右子树不为空,则右子树上的所有节点的值都大于根节点的值;
3. 它的左右子树也分别为二叉搜索树。
二叉搜索树的实现:
查找
插入
删除
树中节点的删除相对麻烦,需要作出如下判断:
- 要删除的节点没有孩子节点;
- 要删除的节点只有左孩子;
- 要删除的节点只有右孩子;
- 要删除的节点既有左孩子,又有右孩子。
我们通常将第一种情况与二或三合并处理。
// 查找待删除节点位置,需要记录双亲节点位置
PNode pCur = _pRoot; // 当前节点为根节点
PNode pParent = nullptr; // 记录双亲节点
// 查找待删除节点
while (pCur){
if (data == pCur->_data)
break;
else if (data < pCur->_data){
pParent = pCur;
pCur = pCur->_pLeft;
}
else{
pParent = pCur;
pCur = pCur->_pRight;
}
}
// 待删除节点为空,直接退出
if (nullptr == pCur)
return false;
// 只有右孩子
if (nullptr == pCur->_pLeft){
// 判断当前节点是否为根节点
if (pCur == _pRoot){
// 是根节点,将根节点替换为待删除节点的右子树
_pRoot = pCur->_pRight;
}
else{
// 否则向下继续遍历右子树
if (pParent->_pLeft == pCur){
pParent->_pLeft = pCur->_pRight;
}
else
pParent->_pRight = pCur->_pRight;
}
}
// 只有左孩子
if (nullptr == pCur->_pRight){
if (pCur == _pRoot){
_pRoot = pCur->_pLeft;
}
else{
if (pParent->_pLeft == pCur){
pParent->_pLeft = pCur->_pLeft;
}
else
pParent->_pRight = pCur->_pLeft;
}
}
对于最后一种情况:
假如要删除值为3的节点。
我们可以查找待删除节点左子树最大的2,或是右子树最小的4来替换待删除节点。
通过此方式,可以将删除有双亲节点的情况,简化为删除叶子节点或是单亲节点的情况。
// 找右子树最小节点
PNode pReplace = pCur->_pRight;
pParent = pCur;
while (pReplace->_pLeft){
pParent = pReplace;
pReplace = pReplace->_pLeft;
}
pCur->_data = pReplace->_data;
// 删除替代节点
if (pReplace == pParent->_pLeft)
pParent->_pLeft = pReplace->_pRight;
else
pParent->_pRight = pReplace->_pRight;
pCur = pReplace;
性能分析:
二叉搜索树的主要操作是查找,所以查找的效率代表了二叉搜索树的性能。
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:logN
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2
代码实现:
https://github.com/Timecur/STL/blob/master/BinarySearchTree.hpp