【C++跬步积累】 —— 二叉搜索树(模拟实现+源代码)

🌏博客主页:PH_modest的博客主页
🚩当前专栏:C++跬步积累
💌其他专栏:
🔴 每日一题
🟡 Linux跬步积累
🟢 C语言跬步积累
🌈座右铭:广积粮,缓称王!


二叉搜索树的概念

二叉搜索树(BST,Binary Search Tree)又称二叉排序数,它或者是一颗空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。
  • 若他的右子树不为空,则右子树上所有节点的值都大于根节点的值。
  • 它的左右子树也分别为二叉搜索树。

注意: 由它的性质可以得出,中序遍历二叉搜索树得到的一组数据是有序的,中序遍历的顺序即是:左 - 根 - 右。

下图就是一个二叉搜索树:
在这里插入图片描述

二叉搜索树操作

在这里插入图片描述

 int  a[ ] = {8,3,1,10,6,4,7,14,13};

二叉搜索树的查找

  • 从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
  • 最多查找高度次,走到空,还没找到,这个值就不存在。

二叉树的插入

  • 树为空,则直接新增节点,赋值给root指针
  • 树不空,按二叉搜索树性质查找插入位置,插入新节点

若不为空树,具体插入操作如下:

  1. 若待插入节点的值小于根节点的值,则需要将节点插入到左子树。
  2. 若待插入节点的值大于根节点的值,则需要将节点插入到右子树。
  3. 若待插入节点的值等于根节点的值,则插入失败。

在这里插入图片描述

二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回,否则要删除的节点可能分下面四种情况:

  1. 要删除的节点无孩子节点
  2. 要删除的节点只有左孩子节点
  3. 要删除的节点只有右孩子节点
  4. 要删除的节点有左、右孩子节点

看起来有待删除节点有4中情况,实际情况1可以与情况2或者3合并起来,因此真正的删除过程如下:

  • 情况2:删除该节点且使删除节点的双亲节点指向被删除节点的左孩子节点——直接删除。
  • 情况3:删除该节点且使删除节点的双亲节点指向被删除节点的右孩子节点——直接删除。
  • 情况4:在它的右子树中寻找中序下的第一个节点(关键码最小),用它的值填补到被删除节点中,再来处理该节点的删除问题——替换法删除

在这里插入图片描述

二叉搜索树的实现

节点类

要实现二叉搜索树,我们首先需要实现一个节点类:

  • 节点类中包含三个成员变量:节点值、左指针、右指针。
  • 节点类当中只需要实现一个构造函数即可,用于构造指定节点值的节点。
template<class K>
struct BSTreeNode
{
   
	K _key;					//节点值
	BSTreeNode<K>* _left;	//左节点
	BSTreeNode<K>* _right;	//右节点

	//构造函数
	BSTreeNode(const K& key = 0)
		:_val(key)
		,_left(nullptr)
		,_right(nullptr)
	{
   }
};

构造函数

构造函数只需要创建一个空树即可:

BSTree()
		:_root(nullptr)
	{
   }

拷贝构造函数

	Node* _Copy(Node* root)
	{
   
		if (root == nullptr)//如果为空,直接返回
		{
   
			return nullptr;
		}
		Node* newnode = new Node(root->_key);//拷贝根节点
		newnode->_left = _Copy(root->_left);//拷贝左子树
		newnode->_right = _Copy(root->_right);//拷贝右子树
		return newnode;//返回拷贝的树
	}
	
	BSTree(const BSTree<K>& t)
	{
   
		_root = _Copy(t._root);//拷贝二叉搜索树
	}

赋值运算符重载函数

在这里提供一个现代写法,使用swap函数,直接交换两个对象的二叉搜索树的根节点的指针,也就间接实现了二叉树的交换。注意是返回对象的引用,这样可以连续赋值,例如:a=b=c;

	BSTree<K>& operater = (BSTree<K> t)
	{
   
		swap(_root,t._root);
		return *this;
	}

析构函数

析构函数可以调用一个_Destory(),通过_Destory()函数来逐个释放每个节点,因为析构函数不方便设置参数,所以可以通过定义一个私有的函数来封装一下,析构函数直接调用即可。

	void _Destory(Node* root)
	{
   
		if (root == nullptr)
		{
   
			return;
		}
		_Destory(root->_left);
		_Destory(root->_right);
		delete root;
	}
	~BSTree()
	{
   
		_Destory(_root);
		_root = nullptr;
	}

中序遍历

中序遍历暴露在外面的函数依旧是不带参数的,所以为了方便,可以定义一个子函数,通过调用子函数来进行封装。
思路:

  • 先判断是否为空,为空则直接退出
  • 然后通过 左 - 根 - 右 来进行遍历,通过该方式得到的数组是递增的
  • 可以通过递归调用,依次往下查找,先递归左子树
  • 当左子树递归到最深处时,会返回,然后遍历根,此时只需要输出当前值即可
  • 然后就是继续遍历右子树

该递归就是根据中序遍历的性质来写的,只需要考虑清楚什么时候输出值即可:遍历完左端点返回之后

	void _Inoder(Node* root)
	{
   
		if (root == nullptr)
		{
   
			return;
		}
		_Inoder(root->_left);
		cout << root->_key << " ";
		_Inoder(root->_right);
	}

	void Inoder()
	{
   
		_Inoder(_root);
		cout << "\n";
	}

插入函数

  • 首先任然需要先判断是不是空树,如果是空树,就直接在空节点上插入
  • 如果不是空树就遍历,按照搜索二叉树的特性,进行判断,直至找到正确的插入的点
  • 这里需要注意的是,需要定一个parent来存储上一个点的地址,便于将待插入的值连接上去
  • 最后连接时判断一下是连在左端点还是右端点
bool Insert(const K& key)
	{
   
		if (_root == nullptr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PH_modest

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值