定义
二叉搜索树:又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树
1、若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
2、若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
3、它的左右子树也分别为二叉搜索树
如图所示,就是一个二叉搜索树,其实二叉搜索树就是在二叉树的基础上加了上面三条特性。
结论:可以看出将二叉排序树 进行中序遍历就是 递增的序列
节点设计
所以节点设计与二叉树基本相同,有左右孩子,有key值,但是又增加一个value值,形成键值对pari。
template<typename K, typename V>
struct BianrySearchNode
{
BianrySearchNode(const K& key, const V& value)
: _key(key)
, _value(value)
, _pLeft(NULL)
, _pRight(NULL)
{}
K _key;
V _value;
BianrySearchNode<K, V>* _pLeft;
BianrySearchNode<K, V>* _pRight;
};
二叉搜索树的设计,只有一个根节点。
//类设计
template<typename K, typename V>
class BinarySearchTree
{
typedef BianrySearchNode<K, V> Node;
private:
Node *_pRoot;
};
插入操作
(1)如果根节点为空的话,直接创建根节点
(2)寻找插入的位置,如果key值大于节点的值,则到右子树中进行查找,如果key值小于节点的值,到左子树中进行查找。直到,找到插入的位置;
如果这个元素已经存在的话,则不进行插入操作;
因为最后找到的位置,肯定为空,所有在查找的过程中需要标记它的父节点。
删除操作
因为二叉搜索树的左右子树都要同样保证是二叉搜索树;所有删除操作有点麻烦,需要考虑 很多情况。
首先查找元素是否在二叉搜索树中,如果不存在,则返回;否则要删除的结点可能分下面四种情况:
(1)、要删除的结点无孩子结点;
(2)、要删除的结点只有左孩子结点;
(3)、要删除的结点只有右孩子结点;
(4)、要删除的结点有左、右孩子结点;
分析:(1)直接删除
(2)删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点;
(3)删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点;
(4)在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,在来处理该结点的删除问题.
(2)(3)的情形
(4)删除的情况
性能分析
插入和删除操作的主体部分是查找,查找效率代表了二叉排序中上各个操作的性能。对有n个结点的二叉排序树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
C(i)为查找到第i个数据元素时关键字的比较次数。
最坏情况下,二叉排序树退化成单支,平均查找长度为O(n),
一般情况下平均查找长度为O(lbn)
代码实现
template<typename K, typename V>
class BinarySearchTree
{
typedef BianrySearchNode<K, V> Node;
public:
BinarySearchTree(Node* node = NULL)
:_pRoot(node)
{}
BinarySearchTree(K* arr, size_t size)
{
for (size_t idx = 0; idx < size; ++idx)
{
this->Insert(arr[idx],arr[idx]);
}
}
BinarySearchTree(const BinarySearchTree<K, V>& bst)
{
_pRoot = _Copy(bst._pRoot);
}
BinarySearchTree<K, V>& operator=(const BinarySearchTree<K, V>& bst)
{
if (this != &bst)
{
this->~BinarySearchTree();
_pRoot = _Copy(bst._pRoot);
}
return *this;
}
~BinarySearchTree()
{
_Destroy(_pRoot);
}
bool Insert(const K& key, const V& value)
{
if (NULL == _pRoot)
{
_pRoot = new Node(key, value);
return true;
}
//找插入的点
Node *pCur = _pRoot;
Node *pParent = NULL;
while (pCur)
{
pParent = pCur;
if (key < pCur->_key)
pCur = pCur->_pLeft;
else if (key > pCur->_key)
pCur = pCur->_pRight;
else
return false;
}
// 插入
pCur = new Node(key, value);
if (key < pParent->_key)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
return true;
}
bool Remove(const K& key)
{
//找出要删除的节点
Node* pCur = _pRoot;
Node* pParent = NULL;
while (pCur)
{
if (key < pCur->_key)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else if (key > pCur->_key)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else
break;
}
//跳出循环,pCur为空,不为空 :可能为根节点,
//1该结点只有左子树 :包含左右子树都为空
//2该节点只有右子树
//3该节点有左右子树
if (pCur)
{
//只有左子树
if (pCur->_pRight == NULL)
{
if (pCur == _pRoot) //删除的节点为根节点,且只有左子树
{
_pRoot = pCur->_pLeft;
}
else
{ //不为根
if (pParent->_pLeft == pCur) //判断在左还是右
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
delete pCur;
}
else if (pCur->_pLeft == NULL) //只有右子树
{
if (pCur == _pRoot)
_pRoot = pCur->_pRight;
else
{
if (pParent->_pLeft == pCur)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
delete pCur;
}
else //左右子树都存在:找右子树中最小的,再把两个的值互换,删除最小的的那个结点
{
//找右子树中最小的
Node* pRightMin = pCur->_pRight;
Node* pRightMinParent = pCur;
while (pRightMin->_pLeft)
{
pRightMinParent = pRightMin;
pRightMin = pRightMin->_pLeft;
}
//不分根节点和非根节点
pCur->_key = pRightMin->_key;
pCur->_value = pRightMin->_value;
if (pRightMin == pCur->_pRight)
pRightMinParent->_pRight = pRightMin->_pRight;
else
pRightMinParent->_pLeft = pRightMin->_pRight;
delete pRightMin;
}
return true;
}
return false;
}
private:
Node*& _Copy(Node*& pRoot)
{
if (pRoot)
{
Node* pNewNode = new Node(pRoot->_key, pRoot->_value);
pNewNode->_pLeft = _Copy(pRoot->_pLeft);
pNewNode->_pRight = _Copy(pRoot->_pRight);
return pNewNode;
}
}
void _Destroy(Node*& pRoot)
{
if (pRoot)
{
_Destroy(pRoot->_pLeft);
_Destroy(pRoot->_pRight);
delete pRoot;
pRoot = NULL;
}
}
private:
Node *_pRoot;
};