动态查找表是在查找过程中动态生成的,也就是对于给定值key,如果查找表中存在关键字等于key的元素,则查找成功;否则,在查找表中插入关键字等于key的元素。动态查找表有不同的实现方法,接下来介绍三种实现方法。
第一种,也就是今天要写的二叉排序树。
1. 二叉排序树的定义
二叉排序树是一种特别的二叉树,它的具体定义如下:
(1)若它的左子树不空,则左子树上所有结点的关键字值均小于它的根节点的关键字值。
(2)若它的右子树不空,则右子树上所有结点的关键字值均大于它的根节点的关键字值。
(3)它的左右子树仍为二叉排序树。
2.二叉排序树的基本操作之查找(Find)
二叉排序树的查找操作比较简单,我们可以根据它的定义来实现。
当二叉排序树不为空时,将给定值与根节点进行比较,若相等,则视为找到;若不相等,则比较给定值与根节点的值的大小,若小于,则前进至左子树;若大于,则前进至右子树。在下一结点中重复此过程,直到查找成功,或向前行进至一空子树时为止。
bool Find(const K& key)
{
PNode _PRoot = GetRoot();
assert(_PRoot);
PNode Pcur = _PRoot;
while (Pcur)
{
if (key == Pcur->_key)
return true;
else if (key < Pcur->_key)
Pcur = Pcur->_PLeft;
else
Pcur = Pcur->_PRight;
}
return false;
}
3.二叉排序树基本操作之插入(Insert)
在二叉排序树中插入一个结点,形成的二叉树仍然是二叉排序树。
要插入一个新的结点,首先需要查找二叉排序树。若查找失败,则插入新结点,在此过程中,我们需要标记双亲结点,根据插入元素的值决定新节点是左子树还是右子树。
bool _InsertNode(const K& key, const V& value)
{
if (_PRoot == NULL)
{
_PRoot = new Node(key, value);
}
PNode Pcur = _PRoot;
PNode pParent = NULL;
while (Pcur)
{
if (key == Pcur->_key)
return false;
else if (key < Pcur->_key)
{
pParent = Pcur;
Pcur = Pcur->_PLeft;
}
else
{
pParent = Pcur;
Pcur = Pcur->_PRight;
}
}
if (pParent)
{
PNode newNode = new Node(key, value);
if (pParent->_key > key)
pParent->_PLeft = newNode;
else
pParent->_PRight = newNode;
}
return true;
}
4.二叉排序树基本操作之删除(Delete)
在进行删除操作时,需要先查找要删除的关键字值是否在二叉排序树中。假设二叉排序树不空,并且要删除的关键字在二叉排序树中,则对于要删除的结点分为4种情况。假设要删除的结点为Del。
(1)Del 结点无左子树和右子树,此时直接删除,并置为NULL即可。
(2)Del结点只有左孩子
(3)Del结点只有右子树
(4)Del结点左右子树都存在
bool _Delete(const K& key)
{
if (_PRoot == NULL)
return false;
PNode Pcur = _PRoot;
PNode pParent = NULL;
while (Pcur)
{
if (Pcur->_key == key)
break;
else if (Pcur->_key < key)
{
pParent = Pcur;
Pcur = Pcur->_PRight;
}
else
{
pParent = Pcur;
Pcur = Pcur->_PLeft;
}
}
if (Pcur)
{
//只有右子树
if (Pcur->_PLeft == NULL)
{
if (Pcur == _PRoot)
{
_PRoot = Pcur->_PRight;
delete Pcur;
}
else
{
if (Pcur == pParent->_PLeft)
pParent->_PLeft = Pcur->_PRight;
else
pParent->_PRight = Pcur->_PRight;
}
}
//只有左子树
else if(Pcur->_PRight == NULL)
{
if (Pcur == _PRoot)
{
_PRoot = Pcur->_PLeft;
delete Pcur;
}
else
{
if (Pcur == pParent->_PLeft)
pParent->_PLeft = Pcur->_PLeft;
else
pParent->_PRight = Pcur->_PLeft;
}
}
else if (Pcur->_PLeft &&Pcur->_PRight)
{
//找中序遍历的第一个结点(右子树的最左侧的结点)
PNode firstOfOrder = Pcur->_PRight;
PNode ppParent = Pcur;//first的双亲结点
while (firstOfOrder->_PLeft)
{
ppParent = firstOfOrder;
firstOfOrder = firstOfOrder->_PLeft;
}
//删除待删除的结点
Pcur->_key = firstOfOrder->_key;
Pcur->_value = firstOfOrder->_value;
//连接替换结点的指针域
if (firstOfOrder == ppParent->_PLeft)
{
ppParent->_PLeft = firstOfOrder->_PRight;
}
else
{
ppParent->_PRight = firstOfOrder->_PRight;
}
delete firstOfOrder;
firstOfOrder = NULL;
}
return true;
}
return false;
}
6. 二叉排序树的查找性能
(1)最坏情况:数据序列为有序排列的,此时二叉排序树为一棵深度为 n 的单支树。此时平均查找长度 ASL=(n+1)/2 ;
(2)最优情况:数据序列无序,与折半查找判定树相似的二叉排序树,此时平均查找长度 ASL=logN;
综上,在一般情况下,其平均查找长度ASL =LogN;