目录
1、前言
2、C中的二叉搜索树
3、封装过程
4、封装后的代码
5、总结
1、前言
我们知道 C 语言是一种面向过程编程,而 C++ 是一种面向对象编程,因此,在我们使用 C++ 进行编程的时候,对于模板的使用通常是不需要我们了解其工作原理的,只需要我们能够对这模板能够使用即可。这也是 C++ 与 C 相比便捷度差异很大的原因。而今天我会带大家一起来将 C 中的二叉搜索树(以下简称:BST)封装成一个模板,以供后续我们的简便使用。
2、C中的二叉搜索树
在我们封装二叉搜索树之前,我们肯定是需要知道 C 中的 BST 是怎么样的?假如想知道详细细节的可以看看我之前的博客:一起来学学简单的特殊的二叉树吧!二叉搜索树,这里我就直接将 C 中的 BST 直接展示给大家,有兴趣的可以看看。
//树的节点
typedef struct TreeNode
{
int val;//值域
struct TreeNode* left;//左子树
struct TreeNode* right;//右子树
}TreeNode;
//销毁函数
void destroy(TreeNode** proot)
{
//后序遍历销毁树
if (*proot == NULL) return;
destroy(&((*proot)->left));
destroy(&((*proot)->right));
free(*proot);
*proot = NULL;
}
//插入函数:根据二叉搜索树的性质
TreeNode* insert(TreeNode* root, int key)
{
//有两种方法:一种是递归,一种是迭代,这里我统一采用采用递归
if (root == NULL)
{
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
newNode->left = NULL;
newNode->right = NULL;
newNode->val = key;
return newNode;
}
//根据二叉搜索树的性质
if (root->val > key)
root->left = insert(root->left, key);
else if (root->val < key)
root->right = insert(root->right, key);
else if (root->val == key)
return NULL;//不能同时出现两个相同的节点
return root;
}
//删除节点:根据二叉搜索树的性质
TreeNode* Delete(TreeNode* root, int key)
{
if (root == NULL)
{
printf("没有这样的节点\n");
return root;
}
if (root->val > key)
{
root->left = Delete(root->left, key);
}
else if (root->val < key)
{
root->right = Delete(root->right, key);
}
else if (root->val == key)
{
//情况一:
if (root->left == NULL && root->right == NULL)
{
free(root);
root = NULL;
return root;
}
//情况二:
else if (root->left && root->right == NULL)
{
TreeNode* left = root->left;
free(root);
return left;
}
else if (root->right && root->left == NULL)
{
TreeNode* right = root->right;
free(root);
return right;
}
//情况三:
else if (root->right && root->left)
{
TreeNode* cur = root->right;
TreeNode* left = root->left;
TreeNode* right = root->right;
while (cur->left) cur = cur->left;
cur->left = left;
free(root);
root = right;
return root;
}
}
return root;
}
3、封装过程
封装的本质其实就是将所有功能都放到一个“百宝箱”中,我们需要用哪个功能就从这个百宝箱拿出相应的工具即可。而在这里起关键作用的是类,其在这里的作用就是一个箱子,我们只需要向这里面丢我们想要的功能即可。
又因为树中存放的数据可以多种多样的:
int, short, long, char, string......
所以这就要求使用 C++ 中的类模板来应对这些各式各样的问题。
所以,我们可以确认基本的框架就是类模板。
第一步:基本框架
//二叉搜索树的节点
template <class T>
struct BSTreeNode
{
BSTreeNode* _right;
BSTreeNode* _left;
T _val;
BSTreeNode() :_val(0), _left(nullptr), _right(nullptr) {}
BSTreeNode(T val) :_val(val), _left(nullptr), _right(nullptr) {}
BSTreeNode(T val, BSTreeNode* left, BSTreeNode* right) :_val(val), _left(left), _right(right) {}
};
//二叉搜索树
template<class T>
class BSTree
{
...
};
第二步:确认内容
我们想要建立一棵树,那肯定需要根节点,而且我们肯定也不希望这个根节点在类外可以被直接访问到,甚至修改根节点。所以我们需要将根节点设置为私有访问成员。
template<class T>
class BSTree
{
//为了使代码看着更简便,所以这里我们进行一个typedef
typedef BSTreeNode node;
public:
...
private:
node* BST;
};
第三步:增添功能
我们知道对于数据结构的基本功能都是增删查改。
首先,我们知道类可以直接修改类中的成员,因此,成员函数就可以不需要关于类中成员的参数。
然后,我们还知道我们可以手动构建一个构造函数和析构函数,在这里我们可以将列表与这部分内容相结合,以及直接用析构函数销毁 BST。
所以我们的代码就能够出来:
template <class T>
class BSTree
{
friend void test();
public:
typedef BSTreeNode<T> node;
public:
BSTree() :BST(nullptr) {}
~BSTree()
{
destroy(BST);
cout << "销毁成功" << endl;
}
//插入节点
bool insert(const T& val)
{
//当二叉搜索树为空时
if (BST == nullptr)
{
node* newnode = new node(val);
if (newnode == nullptr)
{
//防止新节点创建失败
cout << "new node failed" << endl;
exit(-1);
return false;
}
BST = newnode;
return true;
}
node* newnode = new node(val);
if (newnode == nullptr)
{
//防止新节点创建失败
cout << "new node failed" << endl;
exit(-1);
return false;
}
node* cur = BST;//用于遍历二叉搜索树
node* pre = nullptr;//用于记录当前节点的父节点
while (cur)
{
pre = cur;
if (cur->_val > val)
cur = cur->_left;
else if (cur->_val < val)
cur = cur->_right;
else if (cur->_val == val)
{
cout << "newnode is repeated" << endl;
return false;
}
}
if (pre->_val > val)
pre->_left = newnode;
else if (pre->_val < val)
pre->_right = newnode;
return true;
}
//寻找节点
node* find(const T& val)
{
if (BST == nullptr)
{
cout << "BST is empty" << endl;
return nullptr;
}
node* cur = BST;//用于遍历二叉搜索树
while (cur)
{
if (cur->_val > val)
cur = cur->_left;
else if (cur->_val < val)
cur = cur->_right;
else if (cur->_val == val)
return cur;
}
cout << "no such a node" << endl;
return nullptr;
}
//删除节点
bool erase(const T& val)
{
if (BST == nullptr)
{
cout << "BST is empty" << endl;
return false;
}
if (BST->_val == val)
{
if (!BST->_left && !BST->_right)//目标节点为叶节点
{
delete BST;
BST = nullptr;
}
else if (!BST->_left && BST->_right)//目标节点只有右子树
{
node* right = BST->_right;
delete BST;
BST = right;
}
else if (BST->_left && !BST->_right)//目标节点只有左子树
{
node* left = BST->_left;
delete BST;
BST = left;
}
else if (BST->_left && BST->_right)//目标节点既有左子树又有右子树
{
node* rl = BST->_right;
node* right = BST->_right;
while (rl->_left)//寻找cur的右子树的最左子树
rl = rl->_left;
rl->_left = BST->_left;
delete BST;
BST = right;
}
return true;
}
node* cur = BST;//用于遍历二叉搜索树
node* pre = nullptr;//用于记录当前节点的父节点
while (cur)
{
pre = cur;
if (cur->_val > val)
cur = cur->_left;
else if (cur->_val < val)
cur = cur->_right;
else if (cur->_val == val)
{
if (!cur->_left && !cur->_right)//目标节点为叶节点
{
delete cur;
cur = nullptr;
if (pre->_val > val)
pre->_left = nullptr;
else if (pre->_val < val)
pre->_right = nullptr;
}
else if (!cur->_left && cur->_right)//目标节点只有右子树
{
node* right = cur->_right;
delete cur;
cur = nullptr;
if (pre->_val > val)
pre->_left = right;
else if (pre->_val < val)
pre->_right = right;
}
else if (cur->_left && !cur->_right)//目标节点只有左子树
{
node* left = cur->_left;
delete cur;
cur = nullptr;
if (pre->_val > val)
pre->_left = left;
else if (pre->_val < val)
pre->_right = left;
}
else if (cur->_left && cur->_right)//目标节点既有左子树又有右子树
{
node* rl = cur->_right;
while (rl->_left)//寻找cur的右子树的最左子树
rl = rl->_left;
delete cur;
cur = nullptr;
if (pre->_val > val)
pre->_left = rl;
else if (pre->_val < val)
pre->_right = rl;
}
return true;
}
}
cout << "no such a node" << endl;
return false;
}
private:
node* BST;
//后序遍历销毁二叉搜索树
void destroy(node*& t)
{
if (t == nullptr)
return;
destroy(t->_left);
destroy(t->_right);
delete t;
t = nullptr;
}
};
4、封装后的代码
//二叉搜索树的节点
template <class T>
struct BSTreeNode
{
BSTreeNode* _right;
BSTreeNode* _left;
T _val;
BSTreeNode() :_val(0), _left(nullptr), _right(nullptr) {}
BSTreeNode(T val) :_val(val), _left(nullptr), _right(nullptr) {}
BSTreeNode(T val, BSTreeNode* left, BSTreeNode* right) :_val(val), _left(left), _right(right) {}
};
//二叉搜索树
template <class T>
class BSTree
{
friend void test();
public:
typedef BSTreeNode<T> node;
public:
BSTree() :BST(nullptr) {}
~BSTree()
{
destroy(BST);
cout << "销毁成功" << endl;
}
//插入节点
bool insert(const T& val)
{
//当二叉搜索树为空时
if (BST == nullptr)
{
node* newnode = new node(val);
if (newnode == nullptr)
{
//防止新节点创建失败
cout << "new node failed" << endl;
exit(-1);
return false;
}
BST = newnode;
return true;
}
node* newnode = new node(val);
if (newnode == nullptr)
{
//防止新节点创建失败
cout << "new node failed" << endl;
exit(-1);
return false;
}
node* cur = BST;//用于遍历二叉搜索树
node* pre = nullptr;//用于记录当前节点的父节点
while (cur)
{
pre = cur;
if (cur->_val > val)
cur = cur->_left;
else if (cur->_val < val)
cur = cur->_right;
else if (cur->_val == val)
{
cout << "newnode is repeated" << endl;
return false;
}
}
if (pre->_val > val)
pre->_left = newnode;
else if (pre->_val < val)
pre->_right = newnode;
return true;
}
//寻找节点
node* find(const T& val)
{
if (BST == nullptr)
{
cout << "BST is empty" << endl;
return nullptr;
}
node* cur = BST;//用于遍历二叉搜索树
while (cur)
{
if (cur->_val > val)
cur = cur->_left;
else if (cur->_val < val)
cur = cur->_right;
else if (cur->_val == val)
return cur;
}
cout << "no such a node" << endl;
return nullptr;
}
//删除节点
bool erase(const T& val)
{
if (BST == nullptr)
{
cout << "BST is empty" << endl;
return false;
}
if (BST->_val == val)
{
if (!BST->_left && !BST->_right)//目标节点为叶节点
{
delete BST;
BST = nullptr;
}
else if (!BST->_left && BST->_right)//目标节点只有右子树
{
node* right = BST->_right;
delete BST;
BST = right;
}
else if (BST->_left && !BST->_right)//目标节点只有左子树
{
node* left = BST->_left;
delete BST;
BST = left;
}
else if (BST->_left && BST->_right)//目标节点既有左子树又有右子树
{
node* rl = BST->_right;
node* right = BST->_right;
while (rl->_left)//寻找cur的右子树的最左子树
rl = rl->_left;
rl->_left = BST->_left;
delete BST;
BST = right;
}
return true;
}
node* cur = BST;//用于遍历二叉搜索树
node* pre = nullptr;//用于记录当前节点的父节点
while (cur)
{
pre = cur;
if (cur->_val > val)
cur = cur->_left;
else if (cur->_val < val)
cur = cur->_right;
else if (cur->_val == val)
{
if (!cur->_left && !cur->_right)//目标节点为叶节点
{
delete cur;
cur = nullptr;
if (pre->_val > val)
pre->_left = nullptr;
else if (pre->_val < val)
pre->_right = nullptr;
}
else if (!cur->_left && cur->_right)//目标节点只有右子树
{
node* right = cur->_right;
delete cur;
cur = nullptr;
if (pre->_val > val)
pre->_left = right;
else if (pre->_val < val)
pre->_right = right;
}
else if (cur->_left && !cur->_right)//目标节点只有左子树
{
node* left = cur->_left;
delete cur;
cur = nullptr;
if (pre->_val > val)
pre->_left = left;
else if (pre->_val < val)
pre->_right = left;
}
else if (cur->_left && cur->_right)//目标节点既有左子树又有右子树
{
node* rl = cur->_right;
while (rl->_left)//寻找cur的右子树的最左子树
rl = rl->_left;
delete cur;
cur = nullptr;
if (pre->_val > val)
pre->_left = rl;
else if (pre->_val < val)
pre->_right = rl;
}
return true;
}
}
cout << "no such a node" << endl;
return false;
}
private:
node* BST;
//后序遍历销毁二叉搜索树
void destroy(node*& t)
{
if (t == nullptr)
return;
destroy(t->_left);
destroy(t->_right);
delete t;
t = nullptr;
}
};
我们可以对其进行测试一下
按这个顺序向树中进行插入 5 3 7 1 2 4 9 6
运行结果:
删除上面5这个节点
运行结果:
符合我们的预期结果,也证明了我们的 BST 的创建成功!
5、总结
封装时只需要将函数中的关于类中的成员的参数删除,以及考虑我们的成员应该放在哪里就可以了,所以封装还是一个比较简单的操作,希望看完这篇博客的人,都能够学会自己封装 BST。那么期待我们在下一站的相遇!!!