一起来看一下通过类模板将二叉搜索树封装起来!

40 阅读9分钟

目录

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

运行结果:

屏幕截图 2024-12-27 193701.png

删除上面5这个节点

运行结果:

屏幕截图 2024-12-27 193739.png

符合我们的预期结果,也证明了我们的 BST 的创建成功!

5、总结

封装时只需要将函数中的关于类中的成员的参数删除,以及考虑我们的成员应该放在哪里就可以了,所以封装还是一个比较简单的操作,希望看完这篇博客的人,都能够学会自己封装 BST。那么期待我们在下一站的相遇!!!