C++:二叉搜索树模拟实现(KV模型)

前言

 二叉搜索树又称二叉排序树,他对数据有严格的要求,具体表现在以下几个方面:

  1. 如果一个根节点的左子树不为空,则左子树中所有节点的值都必须小于根节点的值;如果它的右子树不为空,则右子树中所有节点的值都必须大于根节点的值。
  2. 它的左右子树也都必须是一个二叉搜索树,也都必须满足第一条。
  3. 二叉搜索树中的每个节点都是唯一的,不允许重复!!!
    在这里插入图片描述

 二叉搜索树的实际应用主要分为K模型和KV模型。

  1. K模型即Key作为关键码,二叉搜索树中只存储Key一个数据。而关键码则是待搜索的值。比如:我们经常通过软件查找是否存在某个单词,是否拼写正确。
  2. KV模型存储的数据中,每个Key对应一个Value,即键值对<Key, Value>。 我们经常通过Key去查找对应的Val.比如:我们通过英文来查找对应的中文,就是一个最常见的KV场景。

模拟实现KV模型

1. 节点封装

由于是KV模型,我们需要存储Key和Value俩个值。同时二叉搜索树也是二叉树,我们需要它的左右节点。因此节点疯转如下:

template<class K, class V>
struct BSTreeNode
{
   
   
	K _key;
	V _value;
	BSTreeNode<K, V>* _left;
	BSTreeNode<K, V>* _right;

	//默认构造函数, 用于后续new创建节点
	BSTreeNode(const K& key, const V& value)
		:_key(key)
		, _value(value)
		, _right(nullptr)
		, _left(nullptr)
	{
   
   }
};

2、前置工作(默认构造、拷贝构造、赋值重载、析构函数等)

接下来是KV模型封装的框架,以及默认构造、拷贝构造、赋值重载、析构函数。比较简单,就直接给出代码了哈。

template<class K, class V>
	class BSTree
	{
   
   
		typedef BSTreeNode<K, V> Node;//节点重命名
	public:
		//默认构造
		BSTree()
			:_root(nullptr)
		{
   
   }

		//拷贝构造
		BSTree(BSTree<K, V>& t)
		{
   
   
			_root = Copy(t._root);
		}

		//赋值重载
		BSTree<K, V>& operator=(BSTree<K, V> t)
		{
   
   
			swap(_root, t._root);
			return *this;
		}

		//析构函数
		~BSTree()
		{
   
   
			Destory(_root);
		}
	private:
		Node* _root = nullptr;
	};
}

2. 数据插入(递归和非递归版本)

首先我们需要查找数据待插入的位置(为了保证插入数据后整体依然是一颗二叉搜索树).。同时查找插入位置时,只有key是有严格要求的,Value只是附带。
即:如果根节点为空,即是待插入数据位置;否则开始查找,如果待插入数据大于根节点往右子树节点走;如果待插入数据小于根节点往左子树节点走。不断循环,直到查找到空节点时,即为数据待插入的位置;如果查找到的大小和待插入数据值相等则返回false(确保二叉搜索树中的每个节点唯一)

【非递归版本】:

bool Insert(const K& key, const V& value)
{
   
   
	if (_root == nullptr)//根节点为空
	{
   
   
		_root = new Node(key, value);
		return true;
	}

	Node* cur = _root;
	Node* parent = nullptr;//后续插入数据链接时,需要和父节点相连
	while (cur)
	{
   
   
		if (cur->_key > key)//待插入数据小于当前节点,往左子树查找
		{
   
   
			parent = cur;
			cur = cur->_left;
		}
		else if(cur->_key < key)//待插入数据大于当前节点,往右子树查找
		{
   
   
			parent = cur;
			cur = cur->_right;
		}
		else//待插入数据等于当前节点,不允许插入
		{
   
   
			return false;
		}
	}

	//链接
	Node* newNode = new Node(key, value); 
	//链接时,我们无法确定插入节点时在父节点的左边还是右边,需要进一步比较
	if (parent->_key > key)
		parent->_left = newNode;
	else
		parent->_right = newNode;
	return true;
}

【递归版本】:

bool InsertR(const K& key, const V& value)
{
   
   
	//由于我们查找位置需要从根节点开始查找,所以这里通过另一个函数来传递实现
	return _InsertR(_root, key, value);
}
	
bool _InsertR(Node*& root, const K& key, const V& value)
{
   
   
	if (root == nullptr)
	{
   
   
		//注意上述我们形参都是引用,所以不用新增Parent节点
		root = new Node(key, value);
		return true;
	}

	if (root->_key > key)//待插入数据小于当前节点,往左子树查找
		return _InsertR(root->_left, key, value);
	else if (root->_key < key)//待插入数据大于当前节点,往右子树查找
		return _InsertR(root->_right, key, value);
	else
		return false;
}

3、数据删除(递归和非递归版本)

3.1 查找待删除节点位置

删除数据,我们首先需要和插入数据一样,先查找到待删除节点。和插入类似就不多说了。

【查找待删除数据】:

bool Erase(const K& key)
{
   
   
	if (_root == nullptr)//为空即不存在待删除数据
		return false;

	Node* cur = _root;
	Node* parent = nullptr;
	while (cur)
	{
   
   
		if (cur->_key > key)//待删除数据小于当前节点,往左子树查找
		{
   
   
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)
评论 49
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小白debug~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值