什么是二叉搜索树?
二叉搜索树又称二叉排序树。(⚠️:空树也是一颗二叉搜索树)
二叉搜索树的性质:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值。
- 他的左右子树也分别为二叉搜索树。
如何验证一个树是不是二叉搜索树?
- 搜索二叉搜索树的中序遍历一定是一个有序的数据集合。
- 二叉搜索树的最左边节点一定是最小的节点。
- 二叉搜索树的最右边节点一定是最大的节点。
二叉搜索树的性能分析:
二叉搜索树的插入和删除都必须先查找,因此,查找效率便代表了二叉搜索树中各个操作的性能。
假设有一个n个节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是节点在二叉搜索树的深度的函数,即节点越深,则比较次数越多。
但是对于一个数据集合,插入顺序不同,可能得到不同结构的二叉搜索树。
例:有数据集合 int a[7] = {3,4,5,6,7,8,9};
1、右单支二叉搜索树{3,4,5,6,7,8,9}
2、完全二叉树{6,4,3,5,8,7,9}
性能分析:
最优情况下,二叉搜索树为完全二叉树,平均比较次数为:lgN(注:这里是log以二为底的N)
最差情况下,二叉搜索树为右单支,平均比较次数为:N/2;
二叉搜索树的模拟实现:
//二叉搜索树(binary search tree)
#include <iostream>
using namespace std;
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;
public:
BSTree()
: _pRoot(nullptr)
{}
bool Insert(const T& data)
{
// 如果为空树
//创建空节点,然后返回该节点
if (nullptr == _pRoot)
{
_pRoot = new Node(data);
return true;
}
// 如果非空
// 1.找待插入节点在树中的位置
Node* pCur = _pRoot;
Node* pParent = nullptr;//记录待插入节点的双亲节点
while (pCur)
{
pParent = pCur;
if (data < pCur->_data)
pCur = pCur->_pLeft;
else if (data > pCur->_data)
pCur = pCur->_pRight;
else
return false;
}
// 2. 插入新节点
pCur = new Node(data);
if (data < pParent->_data)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
return true;
}
bool Delete(const T& data)
{
if (nullptr == _pRoot)
return false;
// 1、找待删除节点的位置
Node* pCur = _pRoot;
Node* 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;
// 2、删除该节点
Node* pDelNode = pCur;
// pCur可能是叶子节点 || 只有右孩子
if (nullptr == pCur->_pLeft)
{
// 所有的叶子节点和只有右孩子的节点
if (nullptr == pParent)
{
// pCur是根节点
_pRoot = pCur->_pRight;
}
else
{
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
}
else if (nullptr == pCur->_pRight)
{
// 只有左孩子
if (nullptr == pParent)
_pRoot = pCur->_pLeft;
else
{
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
}
else
{
// 左右孩子均存在
// 在pCur的右子树中找一个替代节点-->一定是右子树中最小的节点(最左侧节点)
Node* pDel = pCur->_pRight;
pParent = pCur;
while (pDel->_pLeft)
{
pParent = pDel;
pDel = pDel->_pLeft;
}
pCur->_data = pDel->_data;
// 删除替代节点pDel
if (pParent->_pLeft == pDel)
pParent->_pLeft = pDel->_pRight;
else
pParent->_pRight = pDel->_pRight;
pDelNode = pDel;
}
delete pDelNode;
return true;
}
Node* Find(const T& data)
{
Node* pCur = _pRoot;
while (pCur)
{
if (data == pCur->_data)
return pCur;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
return nullptr;
}
// 验证:
//1、中序遍历后的数列一定有序
//这里只给用户一个接口,为了方便用户使用
void InOrder()
{
_InOrder(_pRoot);
}
//最左边的节点一定是最小的
Node* LeftMost()
{
if (nullptr == _pRoot)
return nullptr;
Node* pCur = _pRoot;
while (pCur->_pLeft)
{
pCur = pCur->_pLeft;
}
return pCur;
}
//最右边的节点一定是最大的
Node* RightMost()
{
if (nullptr == _pRoot)
return nullptr;
Node* pCur = _pRoot;
while (pCur->_pRight)
{
pCur = pCur->_pRight;
}
return pCur;
}
private:
//这里把中序遍历的方法给成私有的,是为了让用户不用传参调用函数,方便用户使用
void _InOrder(Node* pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
}
}
private:
Node* _pRoot;
};
void TestBSTree()
{
BSTree<int> t;
int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
for (auto e : a)
t.Insert(e);
t.InOrder();
cout << endl;
cout << t.LeftMost()->_data << endl;
cout << t.RightMost()->_data << endl;
t.Delete(6);
t.InOrder();
cout << endl;
t.Delete(8);
t.InOrder();
cout << endl;
t.Delete(5);
t.InOrder();
cout << endl;
}
int main()
{
TestBSTree();
return 0;
]