说明
为什么要在C++当中单独再次提及数据结构中的二叉树:
map和set特性需要先铺垫二叉搜索树,而二叉搜哦书也是一种树形结构
二叉搜索树的特性了解,有助于更好的理解map和set特性
二叉树中部分面试题有难度
有些OJ使用C语言实现比较麻烦
二叉搜索树
概念
又称二叉排序树,他或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
![](https://i-blog.csdnimg.cn/blog_migrate/c91855a6c4432a6879eafd9e9e8e1f27.png)
操作
查找:
若根节点不为空:
根节点key==查找key,返回ture
根节点key > 查找key,去到其左子树继续查找
根节点key < 查找key,去到其右子树继续查找
否则返回false
插入:
树为空,直接插入,返回true
树不为空,按二叉树性质查找插入位置,插入新节点
删除:
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a、要删除的结点无孩子结点
b、要删除的结点只有左孩子结点
c、要删除的结点只有右孩子结点
d、要删除的结点有左、右孩子结点
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中 再来处理该结点的删除问题
模拟实现(请仔细阅读代码):
#include<iostream>
#include<assert.h>
#include<vector>
#include<vld.h>
using namespace std;
template<class Type>
class BST;
template<class Type>
class BSTNode
{
friend class BST<Type>;
public:
BSTNode(Type d=Type(),BSTNode<Type>*left=nullptr,BSTNode<Type>*right=nullptr)
:data(d),leftChild(left),rightChild(right)
{}
~BSTNode()
{}
private:
Type data;
BSTNode<Type> *leftChild;
BSTNode<Type> *rightChild;
};
template<class Type>
class BST
{
public:
BST():root(nullptr)
{}
~BST()
{
Destroy();
}
public:
BSTNode<Type>* Find(const Type &key)const
{
return Find(root, key);
}
bool Insert(const Type &v)
{
return Insert(root, v);
}
bool Remove(const Type &key)
{
return Remove(root, key);
}
Type Min()const
{
return Min(root);
}
Type Max()const
{
return Max(root);
}
void SortPrint()const
{
SortPrint(root);
}
void Destroy()
{
Destroy(root);
}
protected:
BSTNode<Type>* Find(BSTNode<Type> *t, const Type &key)const
{
if(t==nullptr || t->data==key)
return t;
if(key < t->data)
return Find(t->leftChild, key);
else
return Find(t->rightChild, key);
}
void SortPrint(BSTNode<Type> *t)const
{
if(t != nullptr)
{
SortPrint(t->leftChild);
cout<<t->data<<" ";
SortPrint(t->rightChild);
}
}
Type Min(BSTNode<Type> *t)const
{
assert(t != nullptr);
while(t->leftChild != nullptr)
t = t->leftChild;
return t->data;
}
Type Max(BSTNode<Type> *t)const
{
assert(t != nullptr);
while(t->rightChild != nullptr)
t = t->rightChild;
return t->data;
}
bool Insert(BSTNode<Type> *&t, const Type &v)
{
//1 查找插入位置
BSTNode<Type> *p = t, *pr = nullptr;
while(p != nullptr)
{
if(v == p->data)
return false;
pr = p;
if(v < p->data)
p = p->leftChild;
else
p = p->rightChild;
}
p = new BSTNode<Type>(v);
//2 建立连接
if(pr == nullptr)
{
t = p;
return true;
}
if(v < pr->data)
pr->leftChild = p;
else
pr->rightChild = p;
return true;
}
bool Remove(BSTNode<Type> *&t, const Type &key)
{
//1 查找节点
if(t == nullptr)
return false;
if(key < t->data)
Remove(t->leftChild, key);
else if(key > t->data)
Remove(t->rightChild, key);
else
{
//2删除节点
if(t->leftChild!=nullptr && t->rightChild!=nullptr)
{
BSTNode<Type> *p = t->leftChild;
while(p->rightChild != nullptr)
p = p->rightChild;
t->data = p->data;
Remove(t->leftChild, p->data);
}
else
{
//最多只有一棵子树
BSTNode<Type> *p = t;
if(t->leftChild != nullptr)
t = p->leftChild;
else
t = p->rightChild;
delete p;
}
return true;
}
}
void Destroy(BSTNode<Type> *&t)
{
if(t != nullptr)
{
Destroy(t->leftChild);
Destroy(t->rightChild);
delete t;
t = nullptr;
}
}
private:
BSTNode<Type> *root;
};
应用
k模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
比如给一个简单的单词word,判断该单词是否正确,具体方式如下:
在单词集合中的个每个单词作为key,构建一个二叉搜索树
在二叉树中寻找是否存在,是则正确,否则错误
kv模型:每一个关键码key都有与之对应的value,即<key,value>的键值对,该种方式在现实生活中非常常见,比如英汉词典就是中英对应的关系
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<T>* _pLeft;
BSTNode<T>* _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();
// 根据二叉搜索树的性质查找:找到值为data的节点在二叉搜索树中的位置
PNode Find(const K& key);
bool Insert(const K& key, const V& value)
{
// ...
PNode pCur = _pRoot;
PNode pParent = nullptr;
while (pCur)
{
pParent = pCur;
if (key < pCur->_key)
pCur = pCur->_pLeft;
else if (key > pCur->_key)
pCur = pCur->_pRight; // 元素已经在树中存在
else
return false;
}
// ...
return true;
}
bool Erase(const K& key)
{
// ...
return true;
}
private:
PNode _pRoot;
};
性能分析
插入和删除必须查找,查找效率代表了二叉搜索树中各个操作的性能
![](https://i-blog.csdnimg.cn/blog_migrate/a1bb5b8a0a0f200a5dae5f56edbd68c5.png)