目录
一.二叉搜索树
1.1概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
二.二叉搜索树操作
2.1. 二叉搜索树的查找
2.2二叉搜索树的插入
插入的具体过程如下:
a. 树为空,则直接插入
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
2.3二叉搜索树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题
三.二叉搜索树的实现<k模型>
3.1树的节点类
定义一个k类型的类模板
实现节点类,首先二叉树的左右孩子先实现,俩个k类型的指针,指向左右孩子
_key用来存储数据
最后实现一下节点的构造函数
template <class K>
struct BSTreeNode
{
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
,_right(nullptr)
,_key(key)
{}
};
3.2 树的接口类
二叉搜索树的接口类和list有些相似
list存储的是一个头节点,二叉搜索树存储的是根节点
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
private:
Node* _root = nullptr;
};
3.3 插入
首先,根据二叉搜索树的性质,左子树小于根节点,右子树大于根节点
1.首先判断树是否为空树,如果为空,直接插入即可,然后返回true
2.树不为空,记录一下根节点,然后定义一个父节点(存储父节点)如果我们不记录cur的父节点的话,最终当cur走到空时,无法判断它的父节点是谁,最终无法链接
3.从记录的根节点开始,判断如果比当前节点的数据大,往右子树寻找,小,往左子树走,如果相等,直接返回false,找到位置cur为空,记录他的父节点,然后开始插入
4.插入的时候,先判断要插入的位置在父亲的左边还是右边,插入成功,返回true
bool Insert(const K& key)
{
//如果树为空,直接插入
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;//记录一下根节点
Node* parent = nullptr;//定义父节点
//寻找要插入的位置
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);
if ( key < parent->_key)
{
parent->_left = cur;
}
else if (parent->_key < key)
{
parent->_right = cur;
}
return true;
}
3.4 查找
1.首先先记录一下根节点
2.然后判断根节点是否为查找的数据
3.在然后走循环 遍历查找key,key大于当前节点,往右子树走,小于,往左子树走; 如果相等返回当前节点
4.最后如果树为空或者没找到直接返回空
Node* Find(const K& key)
{
Node* cur = _root;//记录一下根节点
if (key == cur->_key)
{
return cur;
}
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key == key)
{
return cur;
}
}
return nullptr;
}
3.5 删除<重点>
二叉搜索树的删除
1.先首先先记录一下根节点,定义一个记录父节点的节点 parent
2.利用cur对二叉搜索树进行遍历,查找key值
3.找到要删除的位置后,根据上面删除的分析,我们进行以下几点排除,然后进行删除
a:key的左子树为空 所以父节点链接右子树,在这里我们首先判断 当前节点是否根节点(因为根节点的父节点为空),否则,判断删除节点的右节点要连接在父节点的左边还是右边
b:key的右子树为空 所以父节点链接左子树 ,也是先判断是否为根节点,在判断删除节点的左节点要链接在父节点的左边还是右边
c:删除节点左、右都不为空 , 开始找<左子树的最大值节点,或者右子树的最小值节点>
找到之后,对将要删除的节点合照到节点的值进行交换,再将被交换的值的节点进行删除(交换到要删除的节点肯定是叶子节点(没有孩子))
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
找key
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
找到存放 key的节点 进行删除
else
{
key的左子树为空 所以父节点链接右子树
if (cur->_left == nullptr)
{
如果删除的是根节点,此时父节点指向为nullptr
if (parent == nullptr)
{
直接让_root指向下一个节点
_root = _root->_right;
}
else
{
判断应该链接到父节点的左还是右
if (cur == parent->_left)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
delete cur;
}
key的右子树为空 所以父节点链接左子树
else if (cur->_right == nullptr)
{
删除的为根节点的情况
if (parent == nullptr)
{
_root = _root->_left;
}
else
{
判断应当链接到父节点的左还是右
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
删除节点左、右都不为空
else
{
指向cur,防止非法访问
Node* minparent = cur;
Node* min = cur->_right;
while (min->_left != nullptr)
{
minparent = min;
min = min->_left;
}
swap(cur->_key, min->_key);
解决野指针或min->right有子树的情况
minparent->_left = min->_right;
判断min在minparent的左或右
if (minparent->_left == min)
minparent->_left = min->_right;
else
minparent->_right = min->_right;
delete min;
}
return true;
}
}
return false;
}