【基】C/C++二叉搜索树的模拟实现



前言

二叉查找树(Binary Search Tree)或称二叉搜索树,二叉排序树
其可以是一棵空树,或者是具有下列性质的二叉树

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
  • 它的左、右子树也分别为二叉排序树

二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。


1.树节点与树的结构体

  • 节点
template<typename T>
class BSTreeNode
{
public:

	BSTreeNode(const T& key)
		:_key(key)//初始化列表尽量与声明同序
		, _left(nullptr)
		, _right(nullptr)
	{}
	BSTreeNode<T>& operator=(BSTreeNode<T> node)
	{
		_key = node->_key;
		swap(_left, node->_left);
		swap(_right, node->_right);

		return *this;
	}

	BSTreeNode(const BSTreeNode& node)
	{
		_key = node->_key;
		_left = node->_left;
		_right = node->_right;
	}
	T _key;
	BSTreeNode* _left;
	BSTreeNode* _right;
};
  • 树的基本内容
template<typename T>
class BSTree
{
	typedef BSTreeNode<T> Node;
private:
	Node* root;
};


2.树的构造函数

BSTree()//用于创建空白树
		:root(nullptr)
	{}

	BSTree(const BSTree<T>& tree)//拷贝构造整棵树,一般采取递归方式,因为构造函数无法递归,所以采用再嵌套一层子函数
	{
		root=Copy(tree->root);
	}

	Node*& Copy(const Node*& _root)
	{
		if (_root == nullptr)
			return nullptr;
		Node* newroot = new Node(_root->_key);//利用别名 在下一层递归中"_root"变为_root->_right/left,通过别名实现了递归拷贝
		newroot->_left = Copy(_root->_left);
		newroot->_right = Copy(_root->_right);
		return newroot;
	}

在对使用多个单独联系的空间存储的数据进行复制拷贝时,经常会需要通过递归/迭代的思想来实现。
但迭代往往会让代码并不简短;而构造函数一般又不推荐直接进行递归操作,并且在此处传入的为树类型而非树节点类型,直接递归操作的思路并不好明晰;
故此处再次写一个子函数实现递归操作,接收树的根节点并返回拷贝的根节点实现拷贝构造

  • 需要注意的是,接收参数只有一个时递归时不要用this指针的内容,在递归到下一层时,this并不会也自动变化,如下代码错误
//void Copy(const Node*& _root)
	//{
	//	if (_root == nullptr)
	//		return;
	//	
	//	root->_key = _root->_key;
	//	root->_left = _root->_left;
	//	root->_right = _root->_right;
	//	Copy(_root->_left);
	//	Copy(_root->_right);
	//}错误写法 this.root不会在递归时同时变为root->right/left

3.赋值操作符重载

BSTree<T>&  operator=(const BSTree<T>& tree)//赋值重载 swap现代写法
	{
		BSTree<T> tmp =new BSTree<T>(tree);
		swap(root, tmp.root);
		delete tmp;
		return *this;
	}

或者采用如下写 因为写了拷贝构造 此处tree为深拷贝的临时变量 交换完自动销毁

	/*BSTree<T>& operator=(BSTree<T> tree)
	{
		swap(root, tree.root);
		return *this;
	}*/
	

4.插入_insert

常规的查找空节点

void insert(const T& key)//插入
	{
		if (root == nullptr)
			root= new Node(key);//调用构造函数
		else
		{
			Node* parent = nullptr;
			Node* cur = root;

			while (cur)//为空时找到位置
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;

				}
				
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;

				}
				
				else if (key == cur->_key)//已存在值,不插入
					return;

			}
			cur = new Node(key);

			if (parent->_key > key)//判断在节点左右
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
		}
	
	}
	

5.查找_Find

bool Find(const T& key)
	{
		Node* cur = root;//拷贝构造

		while (cur)//为空时找不到
		{
			if (key > cur->_key)
			{
				cur = cur->_right;

			}

			else if (key < cur->_key)
			{
				cur = cur->_left;
			}

			else if (key == cur->_key)//找到
				return true;
		}
		return false;
	}

6.删除_eraser

需要重点关注的即是 在删除节点的度为2时的替换关系 及剩余子树粘接回原树的逻辑

void Eraser(const T& key)
	{
		Node* cur = root;//浅拷贝
		Node* parent = nullptr;
		while (cur)//为空时找不到
		{
			if (key > cur->_key)
			{
				parent = cur;
				cur = cur->_right;

			}

			else if (key < cur->_key)
			{
				parent = cur;
				cur = cur->_left;
			}

				else if (key == cur->_key)//找到
				{
					if (!cur->_left && !cur->_right)//度为0
					{
						if (cur == root)
						{
							root = nullptr;
						}
						else
						{
							delete cur;
							cur = nullptr;
							return true;
						}
					
					}

					else if (!cur->_left && cur->_right)//度为1左为空
					{
						if (cur == root)
						{
							root = nullptr;
							return true;
						}
						else
						{
							if (cur == parent->_left)
							{

								parent->_left = cur->_right;
								delete cur;
								cur = nullptr;
								return true;

							}
							else
							{

								parent->_right = cur->_right;
								delete cur;
								cur = nullptr;
								return true;

							}
						}
						
						

					}
					else if (cur->_left && !cur->_right)//度为1,右为空
					{
						if (cur == root)
						{
							root = nullptr;
							return true;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
								delete cur;
								cur = nullptr;
								return true;
							}
							else
							{
								parent->_right = cur->_left;
								delete cur;
								cur = nullptr;
								return true;
							}
						}
						
					}
					else if (cur->_left && cur->_right)//度为2,寻找替换节点
					{
					
						Node* parent = nullptr;
							Node* minofbig = cur->_right;
							while (minofbig->_left)//右树最小值
							{
								parent = minofbig;//此处的用意是 删除后需要父节点将剩下节点“粘连”回主链
								minofbig = minofbig->_left;
							}
							swap(minofbig->_key, cur->_key);//只需交换值
							
							if (minofbig == parent->_right)//此时minofbig必无左节点
							{
								parent->_right = minofbig->_right;
							}
							else//此时为minofbig无左节点但任有右节点 将右子树接回原树
							{
								parent->_left = minofbig->_right;
							}
						
								cur= minofbig;
								delete cur;
								cur = nullptr;
								return true;
					}

				}
		}
		return false;

	}

7.中序排序

同样是为了符合使用习惯做出的嵌套

	void Inorder()
	{
		_InOrder(root);
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		

		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

8.销毁_Destory

因为this不可显式传参 故再嵌套一层以符合使用习惯

~BSTree()//因为this不可显式调用 故再嵌套一层以符合使用习惯
	{
		_Destory(root);//即this.root
	}
	void _Destory(Node*& root)
	{
		if (root== nullptr)
			return;
	
		_Destory(root->_right);//在下一层递归中"root"变为root->_right,通过别名实现了递归删除
		_Destory(root->_left);
		//到此则说明root的right与left都为nullptr
		delete root;
		cur root= nullptr;//确保程序安全

	}
		

全部代码

#include<iostream>

template<typename T>
class BSTreeNode
{
public:

	BSTreeNode(const T& key)
		:_key(key)//初始化列表尽量与声明同序
		, _left(nullptr)
		, _right(nullptr)
	{}
	BSTreeNode<T>& operator=(BSTreeNode<T> node)
	{
		_key = node->_key;
		swap(_left, node->_left);
		swap(_right, node->_right);

		return *this;
	}

	BSTreeNode(const BSTreeNode& node)
	{
		_key = node->_key;
		_left = node->_left;
		_right = node->_right;
	}
	T _key;
	BSTreeNode* _left;
	BSTreeNode* _right;
};

template<typename T>
class BSTree
{
public:
	typedef BSTreeNode<T> Node;
	BSTree()//用于创建空白树
		:root(nullptr)
	{}

	BSTree(const BSTree<T>& tree)//拷贝构造整棵树,一般采取递归方式,因为构造函数无法递归,所以采用再嵌套一层子函数
	{
		root=Copy(tree->root);
	}

	Node*& Copy(const Node*& _root)
	{
		if (_root == nullptr)
			return nullptr;
		Node* newroot = new Node(_root->_key);//利用别名 在下一层递归中"_root"变为_root->_right/left,通过别名实现了递归拷贝
		newroot->_left = Copy(_root->_left);
		newroot->_right = Copy(_root->_right);
		return newroot;
	}

	//void Copy(const Node*& _root)
	//{
	//	if (_root == nullptr)
	//		return;
	//	
	//	root->_key = _root->_key;
	//	root->_left = _root->_left;
	//	root->_right = _root->_right;
	//	Copy(_root->_left);
	//	Copy(_root->_right);
	//}错误写法 this.root不会在递归时同时变为root->right/left

	BSTree<T>&  operator=(const BSTree<T>& tree)//赋值重载 swap现代写法
	{
		BSTree<T> tmp =new BSTree<T>(tree);
		swap(root, tmp.root);
		delete tmp;
		return *this;
	}

	/*BSTree<T>& operator=(BSTree<T> tree)
	{
		swap(root, tree.root);
		return *this;
	}*/
	//或者这么写 因为写了拷贝构造,此处tree为深拷贝的临时变量 交换完自动销毁

	~BSTree()//因为this不可显式调用 故再嵌套一层以符合使用习惯
	{
		_Destory(root);//即this.root
	}
	void _Destory(Node*& root)
	{
		if (root == nullptr)
			return;

		_Destory(root->_right);//在下一层递归中"root"变为root->_right,通过别名实现了递归删除
		_Destory(root->_left);
		//到此则说明root的right与left都为nullptr
		delete root;
		root = nullptr;//确保程序安全

	}

	void insert(const T& key)//插入
	{
		if (root == nullptr)
			root= new Node(key);//调用构造函数
		else
		{
			Node* parent = nullptr;
			Node* cur = root;

			while (cur)//为空时找到位置
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;

				}
				
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;

				}
				
				else if (key == cur->_key)//已存在值,不插入
					return;

			}
			cur = new Node(key);

			if (parent->_key > key)//判断在节点左右
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
		}
	
	}
	bool Find(const T& key)
	{
		Node* cur = root;//浅拷贝

		while (cur)//为空时找不到
		{
			if (key > cur->_key)
			{
				cur = cur->_right;

			}

			else if (key < cur->_key)
			{
				cur = cur->_left;
			}

			else if (key == cur->_key)//找到
				return true;
		}
		return false;
	}

	bool Eraser(const T& key)
	{
		Node* cur = root;//浅拷贝
		Node* parent = nullptr;
		while (cur)//为空时找不到
		{
			if (key > cur->_key)
			{
				parent = cur;
				cur = cur->_right;

			}

			else if (key < cur->_key)
			{
				parent = cur;
				cur = cur->_left;
			}

			else if (key == cur->_key)//找到
				{
					if (!cur->_left && !cur->_right)//度为0
					{
						if (cur == root)
						{
							root = nullptr;
						}
						else
						{
							delete cur;
							cur = nullptr;
							return true;
						}
					
					}

					else if (!cur->_left && cur->_right)//度为1左为空
					{
						if (cur == root)
						{
							root = nullptr;
							return true;
						}
						else
						{
							if (cur == parent->_left)
							{

								parent->_left = cur->_right;
								delete cur;
								cur = nullptr;
								return true;

							}
							else
							{

								parent->_right = cur->_right;
								delete cur;
								cur = nullptr;
								return true;

							}
						}
						
						

					}
					else if (cur->_left && !cur->_right)//度为1,右为空
					{
						if (cur == root)
						{
							root = nullptr;
							return true;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
								delete cur;
								cur = nullptr;
								return true;
							}
							else
							{
								parent->_right = cur->_left;
								delete cur;
								cur = nullptr;
								return true;
							}
						}
						
					}
					else if (cur->_left && cur->_right)//度为2,寻找替换节点
					{
					
						Node* parent = nullptr;
							Node* minofbig = cur->_right;
							while (minofbig->_left)//右树最小值
							{
								parent = minofbig;//此处的用意是 删除后需要父节点将剩下节点“粘连”回主链
								minofbig = minofbig->_left;
							}
							swap(minofbig->_key, cur->_key);//只需交换值
							
							if (minofbig == parent->_right)//此时minofbig必无左节点
							{
								parent->_right = minofbig->_right;
							}
							else//此时为minofbig无左节点但任有右节点 将右子树接回原树
							{
								parent->_left = minofbig->_right;
							}
						
								cur= minofbig;
								delete cur;
								cur = nullptr;
								return true;
					}

				}
		}
		return false;

	}

	void Inorder()
	{
		_InOrder(root);
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		

		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
	

private:
	Node* root;
};

总结

以上是二叉搜索树基本功能的模拟实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值