二叉搜索树
目录
基础知识
一、性质:(非空树时)
- 若它的左子树不为空,则左子树上所有节点的值都比根小
- 若它的右子树不为空,则右子树上所有节点的值都比根大
- 它的左右子树也分别文二叉搜索树
二、基本操作:
1、查找:
- 若根节点不为空:
- 如果根节点key == 查找key 返回true
- 如果根节点key > 查找key 在其左子树进行查找
- 如果根节点key < 查找key 在其右子树进行查找
2、插入:
- 树为空,则直接插入
- 树不空,按二叉搜索树性质查找插入位置,插入新节点
3、删除:
分为三种情况:
- 情况a:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
- 情况b:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
- 情况c:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中, 再来处理该结点的删除问题
三、性能:
- 最优情况:二叉搜索树为完全二叉树(近似折半查找),查找效率:O(LogN)
- 最差情况:二叉搜索树为单支树(近似顺序查找),查找效率:O(N)
四、应用:
- 判断一个单词是否拼写正确
- 模拟实现一个简单的字典
- log文件中每个异常重复的IP地址各出现多少次
代码实现
template<class T>
struct BSTNode
{
BSTNode(const T& data = T())
:_pLeft(nullptr)
, _pRight(nullptr)
, _data(data)
{ }
BSTNode<T>* _pLeft;
BSTNode<T>* _pRight;
T _data;
};
template<class T>
class BSTree
{
typedef BSTNode<T> Node;
typedef Node* pNode;
public:
BSTree()
:_pRoot(nullptr)
{}
~BSTree()
{
Destroy(_pRoot);
}
pNode Find(const T& data) //查找
{
pNode pCur = _pRoot;
while (pCur)
{
if (data == pCur->_data)
return pCur;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
return nullptr;
}
bool Insert(const T& data) //插入
{
if (_pRoot == nullptr) //树为空
{
_pRoot = new Node(data);
return true;
}
pNode pCur = _pRoot;
pNode pParent = nullptr;
while (pCur)
{
pParent = pCur;
if (data < pCur->_data)
pCur = pCur->_pLeft;
else if (data > pCur->_data)
pCur = pCur->_pRight;
else
return false; //元素已存在
}
pCur = new Node(data);
if (data < pParent->_data)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
return true;
}
bool Erase(const T& data) //删除
{
if (nullptr == _pRoot)
return false;
pNode pCur = _pRoot;
pNode pParent = nullptr;
while (pCur)
{
if (pCur->_data == data)
break;
else if (pCur->_data > data)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else
{
pParent = pCur;
pCur = pCur->_pRight;
}
}
if (nullptr == pCur) //没有该数据
return false;
if (nullptr == pCur->_pRight) //当前节点只有左孩子或左孩子为空,可直接删除
{
if (pCur == _pRoot) //删除根节点
_pRoot = pCur->_pLeft;
else if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
else if (nullptr == pCur->_pLeft) //当前节点只有右孩子,可直接删除
{
if (pCur == _pRoot) //删除根节点
_pRoot = pCur->_pRight;
else if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
else //当前节点左右孩子都存在,不好直接删除,需要在其子树中找一个替代节点
{
/* 寻找替代节点:二选一
1、找其左子树中的最大节点,即左子树中最右侧的节点,
2、找其右子树中最小的节点,即右子树中最小的节点
替代节点找到后,将替代节点中的值交给待删除节点,转换成删除替代节点
*/
pNode del = pCur->_pRight; //在待删节点的右子树中找最小替代节点
pParent = pCur;
while (del->_pLeft) //右子树的最小节点在左边
{
pParent = del;
del = del->_pLeft;
}
pCur->_data = del->_data; //替换数据
if (pParent->_pLeft == del)
pParent->_pLeft = del->_pRight;
else
pParent->_pRight = del->_pRight;
pCur = del;
}
delete pCur;
pCur = nullptr;
return true;
}
void INOrder() //中序遍历
{
InOrder(_pRoot);
}
private:
void InOrder(pNode pRoot) //中序遍历
{
if (pRoot)
{
InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
InOrder(pRoot->_pRight);
}
}
void Destroy(pNode& pRoot) //销毁
{
if (pRoot)
{
Destroy(pRoot->_pLeft);
Destroy(pRoot->_pRight);
pRoot = nullptr;
}
}
private:
pNode _pRoot;
};
--------------------测试代码---------------------
void testptree()
{
BSTree<int> b;
b.Insert(5);
b.Insert(3);
b.Insert(4);
b.Insert(1);
b.Insert(2);
b.Insert(7);
b.Insert(6);
b.Insert(9);
b.Insert(8);
cout << "插入后的中序遍历:";
b.INOrder();
cout << endl;
BSTNode<int>* s = b.Find(5);
cout << s->_data<< endl;
b.Erase(7);
b.Erase(2);
b.Erase(8);
cout << "删除后的中序遍历:";
b.INOrder();
}
int main()
{
testptree();
system("pause");
return 0;
}