目录
平衡二叉搜索树:AVL树(高度平衡树)、红黑树;
一,二叉搜索树定义
二叉搜索树,又称二叉排序树,是经典的数据结构,既具有链表快速插入和删除的特点,也有顺序表快速查找的特点;通过中序遍历可得到有序序列;
- 或为一颗空树;
- 或具有一下性质的二叉树;
- 若其左子树不为空,则左子树所有节点的值都小于根节点的值;
- 若其右子树不为空,则右子树所有节点的值都大于根节点的值;
- 其左右子树也分别为二叉搜索树;
int arr[] = {5,3,4,1,7,8,2,6,0,9}
二,二叉搜索树操作
- 查找,O(logN),最坏O(N);
- 插入,O(logN);
- 删除,O(logN);
二叉搜索树的查找
从根节点开始,将目标值与当前节点比较,若等于当前节点,则直接返回;若比当前节点小则进入左子树进行比较,否则进入右子树进行比较。重复以上步骤直到找到目标值或遇到空节点停止查找。
if(root)
if(root->data == x)
return true;
if(root->data > x)
在其左子树查找
if(root->data < x)
在其右子树查找
else
return false
二叉搜索树的插入
从根节点开始,将新节点与当前节点比较大小,如果比当前节点小则进入左子树进行比较,否则进入右子树。当到达某个空节点时,将新节点插入到该位置。
- 树为空,则直接插入,然后返回true;
- 树不为空,按二叉搜索树性质查找插入位置,插入新节点;
插入新节点10:
1,按照二叉搜索树的性质,查找插入节点的位置
root-->5 5<10 root=root->right parent=root
root-->7 7<10 root=root->right parent=root
root-->8 8<10 root=root->right parent=root
root-->9 9<10 root=root->right parent=root
2,插入新节点
二叉搜索树的删除
- 首先查找元素是否在二叉搜索树中,如不存在,则返回;
- 否则,要删除的节点可能分为以下四种情况;
- 要删除的节点无孩子,直接删除;
- 要删除的节点只有左或右孩子,将其父节点直接指向左或右孩子;
- 要删除的节点左右孩子节点都有,则在其左子树中找到最大的节点或右子树中找到最小的节点,用其值填补到被删除节点,然后在左子树或右子树中删除这个大的节点或最小节点;
二叉搜索树的实现
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()
{
while (_pRoot)
{
Erase(_pRoot->_data);
}
}
pNode _Find(const T& data, pNode& pCur, pNode& pParent)
{
while (pCur)
{
if (data == pCur->_data)
break;
else if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_pleft;
}
else
{
pParent = pCur;
pCur = pCur->_pright;
}
}
return pCur;
}
pNode Find(const T& data)
{
pNode pParent = nullptr;
pNode pCur = _pRoot;
return _Find(key, pCur, pParent);
}
bool Insert(const T& data)
{
if (_pRoot == nullptr)
{
_pRoot = new Node(data);
return true;
}
pNode pParent = nullptr;
pNode pCur = _pRoot;
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)
{
pNode pParent = nullptr;
pNode pCur = _pRoot;
pNode pdata = _Find(data, pCur, pParent);
if (pdata == nullptr)
return false;
if (pCur->_pleft != nullptr && pCur->_pright != nullptr)
{
pParent = pCur;
pCur = pCur->_pleft;
while (pCur->_pright)
{
pParent = pCur;
pCur = pCur->_pright;
}
}
if (pParent != nullptr)
{
if (pCur->_pright == nullptr)
{
if (pCur->_data < pParent->_data)
pParent->_pleft = pCur->_pleft;
else
pParent->_pright = pCur->_pleft;
}
else if (pCur->_pleft == nullptr)
{
if (pCur->_data < pParent->_data)
pParent->_pleft = pCur->_pright;
else
pParent->_pright = pCur->_pright;
}
pdata->_data = pCur->_data;
}
else
{
if (pCur->_pright == nullptr)
_pRoot = pCur->_pleft;
else
_pRoot = pCur->_pright;
}
delete pCur;
pCur = nullptr;
return true;
}
void _InOrder(const pNode pRoot)
{
pNode pCur = pRoot;
if (pCur == nullptr)
return;
_InOrder(pCur->_pleft);
cout << pCur->_data << " ";
_InOrder(pCur->_pright);
}
void InOrder()
{
_InOrder(_pRoot);
}
private:
pNode _pRoot;
};
三,二叉搜索树的应用
K模型
- 即只有key作为关键码(关键码即为待搜索的值),结构中只需存储key即可;
- 如,一个单词word,判断该单词是否拼写正确;
- 以单词集合中每个单词为key,构建一颗二叉搜索树;
- 在二叉搜索树中检索该单词是否存在,存在即正确,不存在即错误;
KV模型
- 每一个关键码key,都有与之对应的值Value,即<Key,Value>键值对;
- 该种方式在生活中非常常见,如英汉字典就是中英文对应关系,通过英文可快速找到对应的中文,英文单词与对应中文<word、chinese>构造一种键值对;再比如统计单词次数,统计成功后,给定单词就可快速找到其出现次数,单词与出现次数<word,count>构成一种键值对;
template <class K, class V>
struct BSTNode
{
BSTNode(const K& key = K(), const V& value = V())
: _pleft(nullptr)
, _pright(nullptr)
, _key(key)
, _value(value)
{}
BSTNode<K, V>* _pleft;
BSTNode<K, V>* _pright;
K _key;
V _value;
};
template <class K, class V>
class BSTree
{
typedef BSTNode<K, V> Node;
typedef Node* pNode;
public:
BSTree()
:_pRoot(nullptr)
{}
~BSTree()
{
while (_pRoot)
{
Erase(_pRoot->_key);
}
}
pNode _Find(const K& key, pNode& pCur, pNode& pParent)
{
while (pCur)
{
if (key == pCur->_key)
break;
else if (key < pCur->_key)
{
pParent = pCur;
pCur = pCur->_pleft;
}
else
{
pParent = pCur;
pCur = pCur->_pright;
}
}
return pCur;
}
pNode Find(const K& key)
{
pNode pParent = nullptr;
pNode pCur = _pRoot;
return _Find(key, pCur, pParent);
}
bool Insert(const K& key, const V& value)
{
if (_pRoot == nullptr)
{
_pRoot = new Node(key, value);
return true;
}
pNode pParent = nullptr;
pNode pCur = _pRoot;
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 Erase(const K& key)
{
pNode pParent = nullptr;
pNode pCur = _pRoot;
pNode pdata = _Find(key, pCur, pParent);
if (pdata == nullptr)
return false;
if (pCur->_pleft != nullptr && pCur->_pright != nullptr)
{
pParent = pCur;
pCur = pCur->_pleft;
while (pCur->_pright)
{
pParent = pCur;
pCur = pCur->_pright;
}
}
if (pParent != nullptr)
{
if (pCur->_pright == nullptr)
{
if (pCur->_key < pParent->_key)
pParent->_pleft = pCur->_pleft;
else
pParent->_pright = pCur->_pleft;
}
else if (pCur->_pleft == nullptr)
{
if (pCur->_key < pParent->_key)
pParent->_pleft = pCur->_pright;
else
pParent->_pright = pCur->_pright;
}
pdata->_key = pCur->_key;
}
else
{
if (pCur->_pright == nullptr)
_pRoot = pCur->_pleft;
else
_pRoot = pCur->_pright;
}
delete pCur;
pCur = nullptr;
return true;
}
private:
pNode _pRoot;
};
四,二叉搜索树的性能分析
- 插入和删除操作都必须先查找,查找的效率代表了二叉搜索树中各个操作的性能;
- 对有n个节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是节点在二叉搜索树的深度的函数,即节点越深,则比较次数越多;
- 但对同一个关键码集合,如各关键码插入的次序不同,可能得到不同结构的二叉搜索树;
最优情况下,二叉搜索树为完全二叉树O(logN),平均比较次数为logN;
最差情况下,二叉搜索树退化为单支树O(N),平均比较次数为N/2;