5. 二叉搜索樹
5.1 二叉搜索樹概念
二叉搜索樹又稱二叉排序樹,它或者是一棵空樹,或者是具有以下性質的二叉樹:
- 若它的左子樹不爲空,則左子樹上所有節點的值都小於根節點的值
- 若它的右子樹不爲空,則右子樹上所有節點的值都大於根節點的值
- 它的左右子樹也分別爲二叉搜索樹
int a [] = {5,3,4,1,7,8,2,6,0,9};
5.2 二叉搜索樹操作
5.2.1 二叉搜索樹的查找
5.2.2 二叉搜索樹的插入
插入的具體過程如下:
- 樹爲空,則直接插入
- 樹不空,按二叉搜索樹性質查找插入位置,插入新節點
- 二叉搜索樹的刪除
首先查找元素是否在二叉搜索樹中,如果不存在,則返回, 否則要刪除的結點可能分下面四種情況:- 要刪除的結點無孩子結點
- 要刪除的結點只有左孩子結點
- 要刪除的結點只有右孩子結點
- 要刪除的結點有左、右孩子結點
看起來有待刪除節點有4種情況,實際情況a可以與情況b或者c合併起來,因此真正的刪除過程如下:
- 情況b:刪除該結點且使被刪除節點的雙親結點指向被刪除節點的左孩子結點
- 情況c:刪除該結點且使被刪除節點的雙親結點指向被刪除結點的右孩子結點
- 情況d:在它的右子樹中尋找中序下的第一個結點(關鍵碼最小),用它的值填補到被刪除節點中,再來處理該結點的刪除問題
5.3 二叉搜索樹的實現
#include<iostream>
using namespace std;
template<class T>
struct BSTNode
{
BSTNode(const T& val=T())
:_left(nullptr)
, _right(nullptr)
, _data(val)
{}
BSTNode<T>* _left;
BSTNode<T>* _right;
T _data;
};
template<class T>
class BSTree{
typedef BSTNode<T> Node;
typedef Node* PNode;
public:
BSTree()
:_pRoot(nullptr)
{}
~BSTree()
{
_pRoot = nullptr;
}
////修改
PNode Find(const T& data){
if (_pRoot == data)
return _pRoot;
else if (_pRoot == nullptr)
return nullptr;
else
{
if (_pRoot->_data > data)
Find(_pRoot->_left);
else
{
Find(_pRoot->_data>_right);
}
}
return nullptr;
}
bool Insert(const T& data)
{
if (_pRoot == nullptr)
{
_pRoot = new Node(data);
return true;
}
PNode cur = _pRoot;
PNode parent = nullptr;
while (cur)
{
parent = cur;
if (cur->_data > data)
cur = cur->_left;
else if (cur->_data < data)
cur = cur->_right;
else
{
return false;
}
}
//插入元素
cur = new Node(data);
if (parent->_data>cur->_data)
parent->_left = cur;
else
parent->_right = cur;
return true;
}
bool Erase(const T& data)
{
if (_pRoot == nullptr)
return false;
PNode cur = _pRoot;
PNode parent = nullptr;
while (cur)
{
if (cur->_data == data)
break;
else if (cur->_data > data)
{
parent = cur;
cur = cur->_left;
}
else
{
parent = cur;
cur = cur->_right;
}
}
//不在樹中
if (cur == nullptr)
return false;
if (!_pRoot->_right|| !_pRoot->_left) {
_pRoot = _pRoot->_left ? _pRoot->_left : _pRoot->_right;
}
else {
PNode cur = _pRoot->_right;
while (cur->_left) {
cur = cur->_left;
}
_pRoot->val = cur->val;//值替換,很巧妙
_pRoot = _pRoot->_right;
_pRoot->_right = deleteNode(cur->val);
}
return true;
}
private:
PNode _pRoot;
};
5.4 二叉搜索樹的應用
- K模型:K模型即只有key作爲關鍵碼,結構中只需要存儲Key即可,關鍵碼即爲需要搜索到的值。
比如:給一個單詞word,判斷該單詞是否拼寫正確,具體方式如下:
以單詞集合中的每個單詞作爲key,構建一棵二叉搜索樹在二叉搜索樹中檢索該單詞是否存在,存在則拼寫正確,不存在則拼寫錯誤。 - KV模型:每一個關鍵碼key,都有與之對應的值Value,即<Key, Value>的鍵值對。該種方式在現實生活中非常常見:比如英漢詞典就是英文與中文的對應關係,通過英文可以快速找到與其對應的中文,英文單詞與其對應的中文<word, chinese>就構成一種鍵值對;再比如統計單詞次數,統計成功後,給定單詞就可快速找到其出現的次數,單詞與其出現次數就是<word, count>就構成一種鍵值對。
比如:實現一個簡單的英漢詞典dict,可以通過英文找到與其對應的中文,具體實現方式如下:
- <單詞,中文含義>爲鍵值對構造二叉搜索樹,注意:二叉搜索樹需要比較,鍵值對比較時只比較Key
- 查詢英文單詞時,只需給出英文單詞,就可快速找到與其對應的key
5.5 二叉搜索樹的性能分析
插入和刪除操作都必須先查找,查找效率代表了二叉搜索樹中各個操作的性能。
對有n個結點的二叉搜索樹,若每個元素查找的概率相等,則二叉搜索樹平均查找長度是結點在二叉搜索樹的深度的函數,即結點越深,則比較次數越多。
但對於同一個關鍵碼集合,如果各關鍵碼插入的次序不同,可能得到不同結構的二叉搜索樹:
最優情況下,二叉搜索樹爲完全二叉樹,其平均比較次數爲:logN
最差情況下,二叉搜索樹退化爲單支樹,其平均比較次數爲:N/2