此篇二叉搜索树是对前面数据结构所学的二叉树进行补充
二叉树搜索树概念
二叉搜索树又称二叉排序树(BST,Binary Search Tree),它可以是空树也可以是含有以下性质的二叉树
1.当它的左子树不为空,则左子树上的所有节点的值都小于根节点的值
2.当它的右子树不为空,则右子树上的所有节点的值都大于根节点的值
3.它的左右子树分别为二叉搜索树
二叉搜索树的操作
1.二叉搜索树的查找
1.从根节点开始比较,查找,比根大则往右边走查找,比根小则往左边查找
2.最多查找高度次,走到空还没找到,这个值就不存在
二叉树的插入
1.树为空,则直接新增节点,赋值给root指针
2.树不为空,按二叉搜索树的性质查找插入位置,插入新节点(注意链接前后关系)
二叉搜索树的删除
首先查找元素是否存在二叉搜索树中,如果不存在,则返回,否则要删除的节点可能分为以下四种情况
1.要删除的结点无孩子结点
2.要删除的结点只有左孩子结点
3.要删除的结点只有右孩子结点
4.要删除的结点有左右孩子结点
实际上2、3情况可以看成要删除的结点有一个孩子
我们细分后的删除操作可以分为以下操作
1.无孩子结点:直接删除
2.有左孩子结点:删除该结点并使删除结点的双亲结点指向被删除结点的左孩子结点
3.有右孩子结点:删除该结点并使删除结点的双亲结点指向被删除结点的右孩子结点
4.有左右孩子结点:在它的右子树中寻找中序下的第一个结点(值最小),用它的值填补到删除结点中,再用来处理该节点的删除—替换法删除
下面我来看下,二叉搜索树的具体实现
#pragma once
#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;
typedef Node* PNode;
public:
BSTree()
: _pRoot(nullptr)
{}
~BSTree()
{
_Destroy(_pRoot);
}
PNode Find(const T& data)
{
return _FindR(_pRoot, data);
}
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 (_pRoot == nullptr)
return false;
PNode pCur = _pRoot;
PNode 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 (pCur == nullptr)
return false;
if (pCur->_pLeft == nullptr)//只有右子树
{
if (pCur == _pRoot)
_pRoot = pCur->_pRight;
else
{
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pLeft;
}
delete pCur;
pCur = nullptr;
}
else if (pCur->_pRight == nullptr)
{
if (pCur == _pRoot)
_pRoot = pCur->_pLeft;
else
{
if (pCur == pParent->_pRight)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pLeft;
}
delete pCur;
pCur = nullptr;
}
else//找到右子树最小结点删除
{
Node* minParent = pCur;
Node* min = pCur->_pRight;
while (min->_pLeft)
{
minParent = min;
min = min->_pLeft;
}
swap(min->_data, pCur->_data);
if (minParent->_pLeft == min)
minParent->_pLeft = min->_pRight;
else
minParent->_pRight = min->_pLeft;
delete min;
return false;
}
return true;
}
void InOrder()
{
_InOrder(_pRoot);
cout << endl;
}
private:
PNode _pRoot;
void _Destroy(PNode& _pRoot)
{
if (_pRoot == nullptr)
return;
_Destroy(_pRoot->_pLeft);
_Destroy(_pRoot->_pRight);
delete(_pRoot);
_pRoot = nullptr;
}
bool _FindR(PNode& _pRoot, const T& data)
{
if (_pRoot == nullptr)
return false;
if (_pRoot->_data < data)
return _FindR(_pRoot->_pRight, data);
else if (_pRoot->_data > data)
return _FindR(_pRoot->_pLeft, data);
else
return true;
}
void _InOrder(PNode _pRoot)
{
if (_pRoot == nullptr)
return;
_InOrder(_pRoot->_pLeft);
cout << _pRoot->_data << " ";
_InOrder(_pRoot->_pRight);
}
};
二叉搜索树的应用
1.K模型:K模型即只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值
2.KV模型:每一个关键码key,都有与之对应的值value,即<Key,Value>的键值对。
比如:英汉词典就是英文与中文的对应关系。<word,chinese>就构成一种键值对。
还可以统计单词次数,统计成功后,给定单词就可以快速找到其出现的次数,<word,count》就构成一种键值对
KV结构的二叉搜索树
#pragma once
#include<iostream>
using namespace std;
template<class K, class V>
struct BSTreeNode
{
BSTreeNode<K, V>* _left;
BSTreeNode<K, V>* _right;
K _key;
V _value;
BSTreeNode(const K& key, const V& value)
:_left(nullptr)
, _right(nullptr)
, _key(key)
, _value(value)
{}
};
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
public:
bool Insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key, value);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key, value);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
bool Erase(const K& key)
{
//...
return true;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << ":" << root->_value << endl;
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
二叉搜索树的性能分析
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中的各个操作的性能
有n结点的二叉树,最小深度为log(n),最大深度为n
当二叉搜索树为完全二叉树(或接近完全二叉树是),效率最高
当二叉搜索树为单插树,效率最低。