提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
这篇博客主要是向大家简单介绍一下二叉搜索树,以及讲述一些较难的函数的实现,为之后规则更“严苛”的平衡二叉搜索树做一个铺垫
一、二叉搜索树
1.概念
(1)如果一颗二叉搜索树的左子树不为空,那么它的左子树上所有结点的值都小于根结点的值
(2)如果一颗二叉搜索树的右子树不为空,那么它的右子树上所有结点的值都大于根结点的值
二叉搜索树可以是一个空树
二、重点函数
如果我们想构造出一棵树的话,得先把节点定义出来,这里我选择把节点的所有成员( 键值,左节点的地址,右节点的地址)全部定义成public成员,以便在我们写二叉搜索树这个类的时候可以自由访问节点成员:
二叉搜索树这个类的私有成员只要有一个_root指针用来保存根节点就可以了
template<class T>
struct BSTNode
{
BSTNode<T>* _pleft;
BSTNode<T>* _pright;
T _data;
BSTNode(const T& data)
:_pleft(nullptr)
, _pright(nullptr)
, _data(data)
{}
};
构造函数和析构函数:
BSTree() = default;//使其使用默认生成的构造函数
~BSTree()
{
Destroy(_root);
}
void Destroy(Node* root)
{
if (root == nullptr)
return;
Destroy(root->_pleft);
Destroy(root->_pright);
delete root;
root = nullptr;
}
1.插入新元素
思路较为简单,如果我们插入的元素比根节点保存的键值大,就再去和右节点比较,如果要插入的元素比根节点保存的键值小,就再去和左节点比较
直到在找到合适的位置时,定义出一个新的节点,将其插入到二叉搜索树里
bool Insert(const T& m)
{
if (_root == nullptr)
{
_root = new Node(m);
return true;
}
Node* parent = _root;
Node* cur = _root;
while (cur)
{
if (cur->_data < m)
{
parent = cur;
cur = cur->_pright;
}
else if (cur->_data > m)
{
parent = cur;
cur = cur->_pleft;
}
else
{
return false;
}
}
cur = new Node(m);
if (parent->_data < m)
{
parent->_pright = cur;
}
else
{
parent->_pleft = cur;
}
return true;
}
2.查找元素
和“插入元素”的思路差不多,从目前这两个函数来看的话,应该很容易就能体会到二叉搜索树查找元素的优势了
bool Find(const T& m)
{
Node* cur = _root;
while (cur)
{
if (cur->_data < m)
{
cur = cur->_pright;
}
else if (cur->_data > m)
{
cur = cur->_pleft;
}
else
{
return true;
}
}
return false;
}
3.删除元素
把删除操作的顺序分为以下几步:
1.如果包含对应键值m的节点存在,找出它的位置,否则直接返回false
2.删除结点的过程需要分3种情况
2.1如果要删除的结点左子树和右子树都为空,直接删除这个节点
2.2如果要删除的结点左子树和右子树中,有且只有一个为空,那么删除这个节点以后,要把要删除的结点的非空子树托管给要删除的结点的父节点
2.3如果要删除的节点的左右节点均不为空,那么删除这个节点的方法可以参考“堆”删除过程;首先我们要找到要删除的结点“右子树中的最小节点”或者“左子树中的最大节点”,用这个节点的值把要删除的结点的值覆盖,然后删除这个节点:
bool Erase(const T& m)
{
if (_root == nullptr)
{
return false;
}
//查找
Node* parent = _root;
Node* cur = _root;
while (cur)
{
if (cur->_data > m)
{
parent = cur;
cur = cur->_pleft;
}
else if (cur->_data < m)
{
parent = cur;
cur = cur->_pright;
}
else
{
//删除
//cur左空
if (cur->_pleft == nullptr)
{
if (cur == _root)
{
_root = cur->_pright;
}
else
{
if (parent->_pleft == cur)
{
parent->_pleft = cur->_pright;
}
else
{
parent->_pright = cur->_pright;
}
}
delete cur;
}
//2.cur右空
else if (cur->_pright == nullptr)
{
if (cur == _root)
{
_root = cur->_pleft;
}
else
{
if (parent->_pleft == cur)
{
parent->_pleft = cur->_pleft;
}
else
{
parent->_pright = cur->_pleft;
}
}
delete cur;
}
else
{
//3.找右子树最小的节点,覆盖cur后删除
Node* pminRight = cur;
Node* minRight = cur->_pright;
while (minRight->_pleft);
{
pminRight = minRight;
minRight = minRight->_pleft;
}
cur->_data = minRight->_data;
pminRight->_pleft = minRight->_pright;
delete minRight;
}
return true;
}
}
return false;
}
我自己写起来的时候2.3这种情况是最让人感觉迷惑的:
当时我想的是,右节点比要删除的结点的键值大,那我在右子树里找比右子树的根节点小的元素的话,万一找出来的元素比要删除的结点的键值更小,二叉搜索树就不成立了
其实这种担忧没有必要,因为根据二叉搜索树的定义是右子树的所有节点的键值都大于根节点,如果你在右子树找到了一个比根节点键值更小的元素的话,说明插入元素的那部分就做错了