二叉排序树及其查找过程
二叉排序树(Binary Sort Tree)或者是一颗空树;或者是具有下列性质的二叉树:
(1)、若它的左子树不空,则左子树上所有结点的值均小于它的根结点;
(2)、若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)、它的左、右子树也分别为二叉排序树;
二叉排序树又称二叉查找树,根据上述定义的结构特点可见,它的查找过程和次优二叉树类似。即当二叉排序树不空时,首先将给定值和根结点的关键字比较,若相等,则查找成功,否则将依据给定值和根结点的关键字之间的大小关系,分别在左子树或游戏书上继续查找。通常,可取二叉链表作为二叉排序树的存储结构,简单的查找算法:
//非递归
bool Find_Nor(const K& key)
{
Node* pCur = _pRoot;
if (NULL == _pRoot)
return false;
while (pCur)
{
if (key < pCur->_key)
pCur = pCur->_pLeft;
else if (key > pCur->_key)
pCur = pCur->_pRight;
else
return true;
}
return false;
}
//递归
bool _Find(Node* pRoot, const K& key)
{
if (NULL == pRoot)
return false;
if (key < pRoot->_key)
return _Find(pRoot->_pLeft, key);
else if (key > pRoot->_key)
return _Find(pRoot->_pRight, key);
else
return true;
}
我们都知道它叫做二叉搜索树(二叉查找树)所以它的查找效率应该是杠杠的;
二叉排序树的插入
二叉排序树是一种动态树表,其特点是,树的结构通常不是一次生成的,而是在查找过程中,当树中不存在关键字等于给定值得结点时再进行插入。新插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个节点的左孩子或者右孩子结点。为此,我们可以在查找的基础上加入添加元素,便可以完成插入;
//非递归
bool Insert_Nor(const K& key, const V& value)
{
Node* _node = new Node(key, value);
if (NULL == _pRoot)
{
_pRoot = _node;
return true;
}
Node* pParent = NULL;
Node* pCur = _pRoot;
while (pCur) //找到新结点要放的位置,所以它是NULL的,需要保存它的双亲
{
if (key < pCur->_key)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else if (key > pCur->_key)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else
{
cout << "数据已存在" << endl;
return false;
}
}
if (key < pParent->_key) //与双亲比较后,存放,满足二叉搜索树的性质
{
pParent->_pLeft = _node;
return true;
}
else
{
pParent->_pRight = _node;
return true;
}
}
//递归
bool _Insert(Node* &pRoot, const K& key, const V& value)
{
if (NULL == pRoot)
{
pRoot = new Node(key, value);
return true;
}
if (key < pRoot->_key)
return _Insert(pRoot->_pLeft, key, value);//return 什么时候使用?
else if (key > pRoot->_key)
return _Insert(pRoot->_pRight, key, value);
else
{
cout << "数据已存在" << endl;
return false;
}
}
二叉排序树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回;否则要删除的结点可能分为下面四种情况:
1、要删除的结点无孩子结点(叶子结点)
2、要删除的结点只有左孩子结点
3、要删除的结点只有右孩子结点
4、要删除的结点左右孩子都有;
对于这4中情况,相应的删除方法如下:
a、直接删除该结点;
b、删除该结点且使被删除结点的双亲结点指向别删除结点的左孩子结点;
c、删除该结点且使被删除结点的双亲结点指向被删除界定啊的右孩子结点;
d、在他的右子树中寻找中序遍历下的第一个结点,用它的值填补到被删除结点中,在来处理该结点的删除问题;
最终:我们将第一种情况规划到2或3中一起处理就可以了;
要删除的结点只有左孩子结点
要删除的结点只有左孩子时,也要分为3中情况:
1、删除的结点为根结点,根结点直接指向它的下一个结点;
2、要删除的结点为双亲结点的左孩子,双亲的左孩子应该指向被删除结点的左孩子;
3、要删除的结点为双亲结点的右孩子,双亲的右孩子应该指向被删除结点的左孩子
下图仅供参考:
要删除的结点只有右孩子结点
同样,要删除的结点只有右孩子结点时,也可以分为上图的三种情况:
要删除的结点左右孩子都有
删除的这三种情况分起来还是比较容易混的,所以画图是一个很好的办法
参考代码:
//非递归
bool Remove_Nor(const K& key) //??????????????????????????????????
{
if (NULL == _pRoot)
return false;
if (NULL == _pRoot->_pLeft && NULL == _pRoot->_pRight)
{
delete _pRoot;
_pRoot = NULL;
return true;
}
Node* pParent = NULL;
Node* pDel = _pRoot;
while (pDel) //查找要删的元素
{
if (key < pDel->_key)
{
pParent = pDel;
pDel = pDel->_pLeft;
}
else if (key > pDel->_key)
{
pParent = pDel;
pDel = pDel->_pRight;
}
else
break;
}
if (pDel)
{
if (pDel->_pLeft == NULL) //没有左子树
{
if (pDel == _pRoot) //如果pDel是根结点
{
_pRoot = pDel->_pRight;
}
else
{
if (pParent->_pLeft == pDel)
pParent->_pLeft = pDel->_pRight;
else
pParent->_pRight = pDel->_pRight;
}
delete pDel;
pDel = NULL;
}
else if (pDel->_pRight == NULL) //没有右子树
{
if (pDel == _pRoot)
_pRoot = pDel->_pLeft;
else
{
if (pParent->_pLeft == pDel)
pParent->_pLeft = pDel->_pLeft;
else
pParent->_pRight = pDel->_pLeft;
}
delete pDel;
pDel = NULL;
}
else
{
//找到右子树中最小的结点
Node* FirstInder = pDel->_pRight;
Node* prve = pDel;
if (FirstInder->_pLeft)
{
prve = FirstInder;
FirstInder = FirstInder->_pLeft;
}
std::swap(FirstInder->_key, pDel->_key);
std::swap(FirstInder->_value, pDel->_value);
if (prve->_pRight == FirstInder)
prve->_pRight = FirstInder->_pRight;
else
prve->_pLeft = FirstInder->_pRight;
delete FirstInder;
FirstInder = NULL;
}
return true;
}
else
{
cout << "没有这个数" << endl;
return false;
}
}
//递归
bool _Remove(Node*& pRoot, const K& key)
{
if (NULL == pRoot)
return false;
if (key < pRoot->_key)
return _Remove(pRoot->_pLeft, key);
else if (key > pRoot->_key)
return _Remove(pRoot->_pRight, key);
else
{
Node* pDel = pRoot;
if (pRoot->_pLeft == NULL)
{
pRoot = pRoot->_pRight;
delete pDel;
pDel = NULL;
}
else if (pRoot->_pRight == NULL)
{
pRoot = pRoot->_pLeft;
delete pDel;
pDel = NULL;
}
else
{
Node* FirstInder = pDel->_pRight;
while (FirstInder->_pLeft)
FirstInder = FirstInder->_pLeft;
std::swap(FirstInder->_key, pDel->_key);
std::swap(FirstInder->_value, pDel->_value);
_Remove(pRoot->_pRight, key);//这里不能传临时变量
}
}
}
一般实现:上面是每个模块的单独代码,完整代码以上传GitHub中:搜索二叉树的插入与删除;
迭代器实现
利用迭代器实现 完整代码GitHub中:迭代器实现搜索二叉树
转化为双向链表
Node* _TreeToList(Node* pRoot, Node*& prev)
{
Node* cur = pRoot;
if (NULL == cur) //树如果为空,退出
return NULL;
_TreeToList(cur->_pLeft, prev);
cur->_pLeft = prev;
if (prev)
{
prev->_pRight = cur;
}
prev = cur;
_TreeToList(cur->_pRight, prev);
}