#pragma once
#include <iostream>
using namespace std;
template <class K>
//为了让外面的内容访问到里面的节点所以就定义成struct
struct SBTreeNode
{
SBTreeNode<K>* _left;
SBTreeNode<K>* _right;
K _key;
SBTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template <class K>
class SBTree
{
typedef SBTreeNode<K> node;
public:
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 为 nullptr,表示找到了合适的插入位置
// 新建节点,并根据大小关系插入到父节点的左或右子树
node* newNode = new node(key);
if (parent->_key > key)
{
parent->_left = newNode;
}
else
{
parent->_right = newNode;
}
return true;
}
bool Find(const K& key)
{
node* cur = _root;
node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else // 键值相等,不允许重复插入
{
return true;
}
return false;
}
}
void InOrder()
{
_InOrder(_root);
}
bool Erase(const K& key)
{
node* cur = _root;
node* parent = cur;//当定义一个空指针时后面就要考虑是否会出现空指针解引用的问题
//整体逻辑是先找到要删除的元素,和上面find一样
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
//找到之后分情况讨论开始删除
//1.待删除节点的左右有节点为空,直接将父节点指向该节点不为空的子节点
//如果是是左为空,还要再进行判断是父节点的左还是右
// 同理,如果是右为空,也要判断是父节点的左还是右
//
//2.待删除节点的左右都不为空,用替代法删除
//左为空
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
//判断是父节点的左,就将父节点的左指向该节点的右
else if (cur == parent->_left)
{
parent->_left = cur->_right;
}
//判断是父节点的右,就将父节点的右指向该节点的右
else
{
parent->_right = cur->_right;
}
delete cur;
cur = nullptr;
}
//右为空
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
//判断是父节点的左,就将父节点的左指向该节点的左
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
//判断是父节点的右,就将父节点的右指向该节点的左
else
{
parent->_right = cur->_left;
}
delete cur;
cur = nullptr;
}
//左右都不为空,用替换法删除,可以用左树的最小节点,也可以用右树的最大节点
//替换节点赋值给删除节点后,删除替换节点
//替换节点要么没有孩子节点,要么只有一个节点,所以可以直接删除
else
{
//这里我们使用右子树的最小节点来进行替换,首先还是一样找到该节点
node* minparent = cur;
node* min = cur->_right;
while (min->_left)
{
minparent = min;
min = min->_left;
}
swap(cur->_key, min->_key);
if (minparent->_left == min)
{
minparent->_left = min->_right;
}
else
{
minparent->_right = min->_right;
}
delete min;
}
return true;
}
return false;
}
}
/*bool InsertR()
{
_InsertR(_root,)
}*/
//这里递归调用同样要用root所以需要再套一层
bool FindR(const K& key)
{
_FindR(_root,key);
}
bool InsertR(const K& key)
{
return _InsertR(_root,key);
}
bool EraseR(const K& key)
{
return _EraseR(_root,key);
}
~SBTree()
{
_Destroy(_root);
}
//强制编译器初始化默认构造
SBTree() = default;
SBTree(const SBTree<K>& t)
{
_root = _copy(t._root);
}
//这里利用现代写法,t是原来的一个拷贝构造出来的临时的,所以出了作用域会自动销毁
SBTree<K>& operator=(SBTree<K> t)
{
swap(_root, t._root);
return *this;
}
private:
node* _root = nullptr;
node* _Copy(node* root)
{
if (root == nullptr)
{
return nullptr;
}
//这里利用递归调用来进行一个前序拷贝
node* copyroot = new node(root->_key);
copyroot->_left = _copy(root->_left);
copyroot->_right = _copy(root->_right);
return copyroot;
}
void _Destroy(node* &root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
root = nullptr;
}
//由于是递归所以必须显示地传子树,不能用this指针,而如果放到public中就无法拿到root,所以这里多套了一层_InOrder,或者重新写一个getroot函数
void _InOrder(node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " " << endl;
_InOrder(root->_right);
}
bool _FindR(node* root ,const K& key)
{
if (root == nullptr)
{
return false;
}
if (root->_key > key)
{
return _FindR(root->_left, key);
}
else if (root->_key < key)
{
return _FindR(root->_right, key);
}
else
{
return true;
}
}
//这里加上引用是因为这样就无法链接上前面的节点,root是一个临时变量是一个形参,所以无法正真链接
//加上引用之后,前面的递归都没有起作用,因为没有对root进行赋值,而在最后一次递归中,root既代表了上一个root的孩子指针的别名,又代表了现在这个空指针
//所以当它赋值的时候,就完成了对上一个指针的赋值,完成了链接
bool _InsertR(node* &root, const K& key)
{
if (root == nullptr)
{
root = new node(key);
return true;
}
if (root->_key > key)
{
return _InsertR(root->_left, key);
}
else if (root->_key < key)
{
return _InsertR(root->_right, key);
}
else
{
return false;
}
}
//这里传引用和前面的理由相同,方便修改
bool _EraseR(node*& root, const K& key)
{
if (root == nullptr)
{
return false;
}
//虽然是递归调用,但是依旧和前面的逻辑一样,判断条件不能少
//先找到key的位置
if (root->_key < key)
{
return _EraseR(root->_right, key);
}
else if (root->_key > key)
{
return _EraseR(root->_left, key);
}
else
{
node* del = root;
//这里的root有两层含义:1.它是父节点的孩子指针的别名 2.它是孩子节点的地址,所以和前面一样传引用就是为了最后进行赋值链接
//左为空
if (root->_left == nullptr)
{
root = root->_right;
}
//右为空
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
//找到右子树的最小节点,然后将其与待删除节点进行替换,最后将替换完之后的节点删除
node* min = root->_right;
while (min->_left)
{
min = min->_left;
}
swap(root->_key, min->_key);
return _EraseR(root->_right, key);
}
delete del;
return true;
}
}
};
搜索二叉树的简单实现
最新推荐文章于 2024-10-07 19:01:55 发布