目录
一,概念
二,实现分析
1. 插入
(1.)非递归版本
(2.)递归版本
2. 打印搜索二叉树
3.查找函数
(1.)非递归版本
(2.)递归版本
4. 删除函数(重难点)
易错点分析,包你学会
(1.)删除目标,没有左右孩子
(2.)删除目标,只有一个孩子
(3.)删除目标,有两个孩子
代码
(1.)非递归版本
(2.)递归版本
5. 析构函数
6.拷贝构造
三,应用
四,搜索二叉树的缺陷及优化
五,代码汇总
结语
一,概念
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值若它的右子树不为空,则右子树上所有节点的值都大于根节点的值它的左右子树也分别为二叉搜索树
为啥又被称作二叉排序树呢? 当该树被层序遍历时,就是升序。
二,实现分析
实验例子:
int a[] = {8, 3, 1, 10, 6, 4, 5, 7, 14, 13};
1. 插入
(1.)非递归版本
a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。b、最多查找高度次,走到到空,还没找到,这个值不存在。
比较简单这里就直接放代码:
template <class K>
class BinarySeaTree_node
{
typedef BinarySeaTree_node<K> BST_node;
public:
BinarySeaTree_node(const K& val)
: _val(val)
,_left(nullptr)
,_right(nullptr)
{}
K _val;
BST_node* _left;
BST_node* _right;
};
template <class T>
class BSTree
{
typedef BinarySeaTree_node<T> BST_node;
private:
BST_node* root = nullptr;
public:
bool Insert(const T& val)
{
BST_node* key = new BST_node(val);
BST_node* cur = root;
BST_node* parent = nullptr;
while (cur)
{
if (key->_val < cur->_val)
{
parent = cur;
cur = cur->_left;
}
else if (key->_val > cur->_val)
{
parent = cur;
cur = cur->_right;
}
else
{
return 0;
}
}
// 查询好位置后,建立链接
if (!root)
{
root = key;
return 0;
}
if (key->_val > parent->_val)
{
parent->_right = key;
}
else
{
parent->_left = key;
}
return 1;
}
};
(2.)递归版本
这里面整了个活,大家注意了!!!
bool Re_Insert(const T& val){ return Re_Insert_table(root, val);}
bool Re_Insert_table(BST_node*& node, const T& val)
{
if (node == nullptr)
{
node = new BST_node(val);
return 1;
}
if (val < node->_left)
{
return Re_Insert_table(node->_left, val);
}
else if (val > node->_right)
{
return Re_Insert_table(node->_right, val);
}
else
{
return 0;
}
}
这里方便大家理解,我给大家花一个递归展开图。
2. 打印搜索二叉树
a. 树为空,则直接新增节点,赋值给root指针b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
这里也是仅做代码分享:
void Print_table() { Re_Print(root); }
void Re_Print(const BST_node* node)
{
if (node == nullptr)
return;
Re_Print(node->_left);
cout << node->_val << " ";
Re_Print(node->_right);
}
3.查找函数
思路:其实也没啥思路,比父结点小,就找左边,否则找右边。
(1.)非递归版本
BST_node* Find(const T& val)
{
//直接跟寻找位置一样
BST_node* parent = nullptr;
BST_node* cur = root; // 以返回cur的方式返回
while (cur) // 非递归版本
{
if (val < cur->_val)
{
parent = cur;
cur = cur->_left;
}
else if (val > cur->_val)
{
parent = cur;
cur = cur->_right;
}
else
{
return cur;
}
}
return cur;
}
(2.)递归版本
BST_node* Re_Find(const T& val){ return Re_Find_table(root, val); }
BST_node* Re_Find_table(BST_node* node, const T& val)
{
if (node == nullptr)
return nullptr;
if (val < node->_val)
{
return Re_Find_table(node->_left, val);
}
else if (val > node->_val)
{
return Re_Find_table(node->_right, val);
}
else
{
return node;
}
}
4. 删除函数(重难点)
我们简单寻找了一下思路,如下:
但这些思路只是大概方向,其中藏着许多的坑点,诺接下来我来带大家,对这些易错点进行分析。
首先是查询到目标:
这个比较简单,这里不做解释。
//首先寻找到目标,并且记录到parent
BST_node* parent = nullptr;
BST_node* cur = root;
while (cur)
{
if (val < cur->_val)
{
parent = cur;
cur = cur->_left;
}
else if (val > cur->_val)
{
parent = cur;
cur = cur->_right;
}
else
{
break;
}
}
if (!cur)
{
return 0;
}
易错点分析,包你学会
(1.)删除目标,没有左右孩子
(2.)删除目标,只有一个孩子
一般的思路:
但,这是有漏洞的!
诺:
(3.)删除目标,有两个孩子
好啦,前菜上完了来看看,本次的大菜。
代码
(1.)非递归版本
bool Erase(const T& val)
{
//首先寻找到指定值,并且记录到parent
BST_node* parent = nullptr;
BST_node* cur = root;
while (cur)
{
if (val < cur->_val)
{
parent = cur;
cur = cur->_left;
}
else if (val > cur->_val)
{
parent = cur;
cur = cur->_right;
}
else
{
break;
}
}
if (!cur)
{
return 0;
}
// 查询成功,开始删除
if (!cur->_left && !cur->_right) // cur没有左右孩子
{ // 当要删除目标是根
if (cur == root)
{
root = nullptr;
delete cur;
}
// 判断cur是左右孩子
else if (cur->_val < parent->_val)
{
parent->_left = nullptr;
delete cur;
}
else
{
parent->_right = nullptr;
delete cur;
}
return 1;
}
else if (!cur->_left || !cur->_right) // 只有一个孩子
{
if (!parent) // 判断是否是目标是根
{
root = cur->_left != nullptr ? cur->_left : cur->_right;
delete cur;
}
// 判断cur为啥孩子
else if (cur->_val < parent->_val) // 左侧
{
parent->_left = cur->_left != nullptr ? cur->_left : cur->_right;
delete cur;
}
else // 右侧
{
parent->_right = cur->_left != nullptr ? cur->_left : cur->_right;
delete cur;
}
}
else // 有2个孩子
{ // 使用左侧最大的孩子来领养
// 寻找左侧最大
BST_node* maxnode = cur->_left;
BST_node* max_parent = cur;
while (maxnode->_right)
{
max_parent = maxnode;
maxnode = maxnode->_right;
}
// 现在又进入一种特殊情况,1.max_parent就没进入循环,2.进入了循环
if (max_parent == cur)
{
max_parent->_left = maxnode->_left;
}
else
{
max_parent->_right = maxnode->_left;
}
// 值转移
cur->_val = maxnode->_val;
delete maxnode;
}
return 1;
}
(2.)递归版本
bool Re_Erease( const T& val)
{
return Re_Erease_table(root, val);
}
bool Re_Erease_table(BST_node*& node, const T& val)
{
// 首先我们先找到值
if (node == nullptr)
{
return 0; // 如果访问到了空,则说明删除失败,原因是:不存在
}
if (val < node->_val)
{
return Re_Erease_table(node->_left, val);
}
else if (val > node->_val)
{
return Re_Erease_table(node->_right, val);
}
else
{
// 开始删除目标数据。方法如下;
// 1. 就按照非递归的思路,不用改多少代码
// 2. 使用递归方法,优势就是代码简洁
// 这里使用方法2
BST_node* del = node; // 保存每次访问的对象,如果是目标,就备份好了
if (node->_left == nullptr)
{
node = node->_right;
}
else if (node->_right == nullptr)
{
node = node->_left;
}
else
{
//处理左右都有孩子的目标
// 左侧查找最大值,右侧查找最小值
BST_node* max_node = node->_left;
while (max_node->_right)
{
max_node = max_node->_right;
}
// 完成循环后,max_node最多有左孩子,然后数据交换,我们以目标左侧树为起点
// 再次递归删除替换数据。
swap(max_node->_val, node->_val);
return Re_Erease_table(node->_left, val); //已经完成删除,就直接退出,以免触发删除delete
}
//处理前两种情况
delete del;
}
}
5. 析构函数
思路:
~BSTree()
{
Distroy_Re(root);
root = nullptr;
}
void Distroy_Re(BST_node*& node) // 我们采用递归删除
{
if (node == nullptr)
return ;
// 先处理左右孩子
Distroy_Re(node->_left);
Distroy_Re(node->_right);
delete node;
node = nullptr;
}
6.拷贝构造
// 我们实现了拷贝构造,默认构造函数则不会生成
// 解决方案:1.实现构造函数 2.使用default关键字,强制生成默认构造
BSTree()
{
}
// BSTree() = default
BSTree(const BSTree& Tree) // 拷贝构造
{
root = copy(Tree.root);
}
BST_node* copy(BST_node* root)
{
if (root == nullptr)
return nullptr;
BST_node* new_node = new BST_node(root->_val);
new_node->_left = copy(root->_left);
new_node->_right = copy(root->_right);
return new_node;
}
三,应用
四,搜索二叉树的缺陷及优化
最坏情况:N
平均情况:O(logN)
五,代码汇总
namespace key
{
template <class K>
class BinarySeaTree_node
{
typedef BinarySeaTree_node<K> BST_node;
public:
BinarySeaTree_node(const K& val)
: _val(val)
,_left(nullptr)
,_right(nullptr)
{}
K _val;
BST_node* _left;
BST_node* _right;
};
template <class T>
class BSTree
{
public:
typedef BinarySeaTree_node<T> BST_node;
// 我们实现了拷贝构造,默认构造函数则不会生成
// 解决方案:1.实现构造函数 2.使用default关键字,强制生成默认构造
BSTree()
{
}
// BSTree() = default
BSTree(const BSTree& Tree) // 拷贝构造
{
root = copy(Tree.root);
}
BSTree<T>& operator=(BSTree<T> t)
{
swap(root, t.root);
return *this;
}
BST_node* copy(BST_node* root)
{
if (root == nullptr)
return nullptr;
BST_node* new_node = new BST_node(root->_val);
new_node->_left = copy(root->_left);
new_node->_right = copy(root->_right);
return new_node;
}
bool Re_Insert(const T& val) { return Re_Insert_table(root, val); }
void Re_Print() { Re_Print_table(root); }
bool Re_Erease(const T& val) { return Re_Erease_table(root, val); }
BST_node* Re_Find(const T& val) { return Re_Find_table(root, val); }
bool Insert(const T& val)
{
BST_node* key = new BST_node(val);
BST_node* cur = root;
BST_node* parent = nullptr;
while (cur)
{
if (key->_val < cur->_val)
{
parent = cur;
cur = cur->_left;
}
else if (key->_val > cur->_val)
{
parent = cur;
cur = cur->_right;
}
else
{
return 0;
}
}
// 查询好位置后,建立链接
if (!root)
{
root = key;
return 0;
}
if (key->_val > parent->_val)
{
parent->_right = key;
}
else
{
parent->_left = key;
}
return 1;
}
BST_node* Find(const T& val)
{
//直接跟寻找位置一样
BST_node* parent = nullptr;
BST_node* cur = root; // 以返回cur的方式返回
while (cur) // 非递归版本
{
if (val < cur->_val)
{
parent = cur;
cur = cur->_left;
}
else if (val > cur->_val)
{
parent = cur;
cur = cur->_right;
}
else
{
return cur;
}
}
return cur;
}
bool Erase(const T& val)
{
//首先寻找到指定值,并且记录到parent
BST_node* parent = nullptr;
BST_node* cur = root;
while (cur)
{
if (val < cur->_val)
{
parent = cur;
cur = cur->_left;
}
else if (val > cur->_val)
{
parent = cur;
cur = cur->_right;
}
else
{
break;
}
}
if (!cur)
{
return 0;
}
// 查询成功,开始删除
if (!cur->_left && !cur->_right) // cur没有左右孩子
{ // 当要删除目标是根
if (cur == root)
{
root = nullptr;
delete cur;
}
// 判断cur是左右孩子
else if (cur->_val < parent->_val)
{
parent->_left = nullptr;
delete cur;
}
else
{
parent->_right = nullptr;
delete cur;
}
return 1;
}
else if (!cur->_left || !cur->_right) // 只有一个孩子
{
if (!parent) // 判断是否是目标是根
{
root = cur->_left != nullptr ? cur->_left : cur->_right;
delete cur;
}
// 判断cur为啥孩子
else if (cur->_val < parent->_val) // 左侧
{
parent->_left = cur->_left != nullptr ? cur->_left : cur->_right;
delete cur;
}
else // 右侧
{
parent->_right = cur->_left != nullptr ? cur->_left : cur->_right;
delete cur;
}
}
else // 有2个孩子
{ // 使用左侧最大的孩子来领养
// 寻找左侧最大
BST_node* maxnode = cur->_left;
BST_node* max_parent = cur;
while (maxnode->_right)
{
max_parent = maxnode;
maxnode = maxnode->_right;
}
// 现在又进入一种特殊情况,1.max_parent就没进入循环,2.进入了循环
if (max_parent == cur)
{
max_parent->_left = maxnode->_left;
}
else
{
max_parent->_right = maxnode->_left;
}
// 值转移
cur->_val = maxnode->_val;
delete maxnode;
}
return 1;
}
~BSTree()
{
Distroy_Re(root);
root = nullptr;
}
protected:
bool Re_Insert_table(BST_node*& node, const T& val)
{
if (node == nullptr)
{
node = new BST_node(val);
return 1;
}
if (val < node->_val)
{
return Re_Insert_table(node->_left, val);
}
else if (val > node->_val)
{
return Re_Insert_table(node->_right, val);
}
else
{
return 0;
}
}
void Re_Print_table(const BST_node* node)
{
if (node == nullptr)
return;
Re_Print_table(node->_left);
cout << node->_val << " ";
Re_Print_table(node->_right);
}
BST_node* Re_Find_table(BST_node* node, const T& val)
{
if (node == nullptr)
return nullptr;
if (val < node->_val)
{
return Re_Find_table(node->_left, val);
}
else if (val > node->_val)
{
return Re_Find_table(node->_right, val);
}
else
{
return node;
}
}
bool Re_Erease_table(BST_node*& node, const T& val)
{
// 首先我们先找到值
if (node == nullptr)
{
return 0; // 如果访问到了空,则说明删除失败,原因是:不存在
}
if (val < node->_val)
{
return Re_Erease_table(node->_left, val);
}
else if (val > node->_val)
{
return Re_Erease_table(node->_right, val);
}
else
{
// 开始删除目标数据。方法如下;
// 1. 就按照非递归的思路,不用改多少代码
// 2. 使用递归方法,优势就是代码简洁
// 这里使用方法2
BST_node* del = node; // 保存每次访问的对象,如果是目标,就备份好了
if (node->_left == nullptr)
{
node = node->_right;
}
else if (node->_right == nullptr)
{
node = node->_left;
}
else
{
//处理左右都有孩子的目标
// 左侧查找最大值,右侧查找最小值
BST_node* max_node = node->_left;
while (max_node->_right)
{
max_node = max_node->_right;
}
// 完成循环后,max_node最多有左孩子,然后数据交换,我们以目标左侧树为起点
// 再次递归删除替换数据。
swap(max_node->_val, node->_val);
return Re_Erease_table(node->_left, val); //已经完成删除,就直接退出,以免触发删除delete
}
// 查找到删除数据
delete del;
}
}
void Distroy_Re(BST_node*& node) // 我们采用递归删除
{
if (node == nullptr)
return;
// 先处理左右孩子
Distroy_Re(node->_left);
Distroy_Re(node->_right);
delete node;
node = nullptr;
}
private:
BST_node* root = nullptr;
};
}
结语
本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力