关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是
键值对:
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值, value表示与key对应的信息。
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
二叉搜索树的查找:
二叉搜索树的插入数据:
二叉搜索树的删除结点:
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
#include
#include
#include
using namespace std;
template
struct BSTreeNode {
BSTreeNode(const T& data=T())
:_pLeft(nullptr)
,_pRight(nullptr)
,_data(data){}
BSTreeNode* _pLeft;
BSTreeNode* _pRight;
T _data;
};
template
class BSTree {
typedef BSTreeNode Node;
public:
BSTree()
:_pRoot(nullptr)
{}
~BSTree() {
_Destory(_pRoot);
}
bool Insert(const T& data) {
//空树
if(_pRoot ==nullptr) {
_pRoot =new Node(data);
return true;
}
//非空树
//1.找到插入位置
Node* pCur=_pRoot;
Node* pParent = nullptr;
while(pCur) {
pParent = pCur;
else if(data < pCur->_data)
pCur=pCur->_pLeft;
else
pCur=pCur->_pRight;
else
return false;
}
//2.插入数据
pCur = new Node(data);
if(data < pParent->_data) {
pParent->_pLeft = pCur;
}
else
pParent->_pRight = pCur;
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 NuLL;
}
//获取最左侧节点
Node* LeftMost() {
return _LeftMost(_pRoot);
}
//获取最右侧节点
Node* RightMost() {
return _RightMost(_pRoot);
}
//中序遍历
void InOrder() {
_InOrder(_pRoot);
}
//销毁
void _Destory(Node* pRoot) {
if(_pRoot) {
_Destory(_pRoot->_pLeft);
_Destory(pRoot->_pRight);
delete pRoot;
pRoot = nullptr;
}
}
//删除结点
void Delete(const T& data) {
if(_pRoot == nullptr) {
return;
}
//1.找到删除的位置
Node* pCur = _pRoot;
Node* pParent = nullptr;
while(pCur) {
if(data < pCur->data) {
//注意这个位置
pParent = pCur;
pCur=pCur->_pLeft;
}
else if(data > pCur->_pLeft) {
//注意这个位置
pParent = pCur;
pCur=pCur->_pRight;
}
//找到删除的位置了,改删除了
else
break;
}
//2.删除结点
if(pCur == nullptr) {
//节点不存在
return;
}
//分情况删除——四种情况
//1.左右孩子都不存在
//2.左右孩子都存在
//3.只有左孩子
//4.只有右孩子
Node* pDelNode = pCur;
if(pCur->_pRight == nullptr) {
//叶子结点 || 只有左孩子
if(pParent == nullptr) {
//就是根了
_pRoot->pCur->_pLeft;
}
else {
if(pCur == pParent->pLeft) { //是双亲的左
pParent->_pLeft = pCur->_pLeft;
}
else {
pParent->_pRight = pCur->_pLeft;
}
}
}
else if(pCur->_pLeft == nullptr) {
//只有右孩子
if(pParent == nullptr) {
//就是根了
_pRoot->pCur->_pRight;
}
else {
if(pCur == pParent->pLeft) { //是双亲的左
pParent->_pLeft = pCur->_pRight;
}
else {
pParent->_pRight = pCur->_pRight;
}
}
}
else {
//左右孩子都存在,不能直接删除,要找替代节点
//方式一:左子树最大的结点
//方式二:右子树中最小的结点
//在右子树中进行查找
Node* pMostLeft = pCur->_pRight;
pParent = pCur;
while(pMostLeft->_pLeft) {
pParent = pMostLeft;
pMostLeft = pMostLeft -> _pLeft;
}
pCur -> _data = pMostLeftp ->_data;
//删除替代结点
if(pMostLeft == pParent->_pLeft) {
pParent->_pLeft = pMostLeft->_pRight;
}
else {
pParent->_pRight = pMostLeft->_pRight;
}
pDelNode = pMostLeft;
}
delete pDelNode;
return true;
}
private:
Node* _LeftMost(Node* pRoot) {
if(pRoot == nullptr) {
return nullptr;
}
Node* pCur = pRoot;
while(pCur->_pLeft) {
pCur = pCur->_pLeft;
}
return pCur;
}
Node* _RightMost(Node* pRoot) {
if(pRoot == nullptr) {
return nullptr;
}
Node* pCur = pRoot;
while(pCur->_pRight) {
pCur = pCur->_pRight;
}
return pCur;
}
void _InOrder(Node* pRoot) {
if(pRoot){
_InOrder(pRoot->_pLeft);
cout<data<<" ";
_InOrder(pRoot->_pRight);
}
}
private:
Node* _pRoot;
}
二叉数的性能分析:
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:完全二叉树和单支。
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log2 N
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2
问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码, 都可以是二叉搜索树的性能佳?(这也就是二叉搜索树的缺陷)