二叉搜索树的简单实现

目录

1.什么是二叉搜索树

2.二叉搜索树的查找

3.二叉搜索树的插入

4.二叉搜索树的删除

5.完整代码实现

6.二叉搜索树的应用

7.二叉搜索树的性能分析


1.什么是二叉搜索树

二叉搜索树是一棵具有以下性质的二叉树:

1.若左子树不为空,则左子树上所有节点的值都小于根节点的值。

2.若右子树不为空,则右子树上所有节点的值都大于根节点的值。

2.二叉搜索树的查找

二叉搜索树进行查找时,会先从根节点开始比较:

1.如果查找值小于当前节点值,那么就与当前节点的左子树比较。

2.如果查找值大于当前节点值,那么就与当前节点的右子树比较。

直到查找值等于当前节点值或者当前节点为空为止。

代码实现:

Node* Find(const K& key)
{
	if (_root == nullptr) return nullptr;
	Node* cur = _root;
	while (cur->_key != key)
	{
		if (key < cur->_key) cur = cur->leftchild;
		else if(key > cur->_key) cur = cur->rightchild;
		if (cur == nullptr) return nullptr;
	}
	return cur;
}

其中K是键值类型的模版参数。

3.二叉搜索树的插入

二叉搜索树插入之前会先判断树中是否已有插入的值,如果没有则进行插入。比较方式同上,找到合适的位置,保证二叉搜索树的性质。

代码实现:

bool Insert(const K& key, const V& value)
{
	if (Find(key) == nullptr)
	{
		Node* newnode = new Node(key, value);
		if (_root == nullptr) _root = newnode;
		else
		{
			Node* pre = nullptr, *cur = _root;
			while (cur != nullptr)
			{
				pre = cur;
				if (key < cur->_key) cur = cur->leftchild;
				else cur = cur->rightchild;
			}
			if (key < pre->_key) pre->leftchild = newnode;
			else pre->rightchild = newnode;
		}
		return true;
	}
	return false;
}

4.二叉搜索树的删除

二叉搜索树的删除需要分情况讨论:

1.删除节点没有子树:处理父节点与当前节点的关系,即用空指针代替当前节点。删除当前节点。特殊处理根结点。

2.删除节点有1棵子树:处理父节点与当前节点的关系,用当前节点的子树代替当前节点。删除当前节点。特殊处理根结点。

实际上面第1种情况可以并到第2种中。

3.删除节点有2棵子树:找到左子树中的最大值或者右子树中的最小值,处理相关父节点与子节点的关系,用找到的节点代替当前节点。删除当前节点。

代码实现:

bool Erase(const K& key)
{
	if (_root == nullptr) return false;
	//找到删除节点及其父节点
	Node* pre = nullptr, * cur = _root;
	while (cur->_key != key)
	{
		pre = cur;
		if (key < cur->_key) cur = cur->leftchild;
		else if (key > cur->_key) cur = cur->rightchild;
		if (cur == nullptr) return false;
	}
	//删除节点的子树<=1的情况
	if (cur->leftchild == nullptr || cur->rightchild == nullptr)
	{
		Node* tmp = cur->leftchild;
		if (cur->leftchild == nullptr) tmp = cur->rightchild;
		if (cur == _root) _root = tmp;
		else
		{
			if (cur->_key < pre->_key) pre->leftchild = tmp;
			else pre->rightchild = tmp;
		}
	}
	//删除节点的子树为2
	else
	{
		Node* tmp = cur->leftchild,*t=cur;
		while (tmp->rightchild != nullptr)
		{
			t = tmp;
			tmp = tmp->rightchild;
		}
		swap(tmp->_key, cur->_key);
		swap(tmp->_value, cur->_value);
		if (t->leftchild == tmp) t->leftchild = tmp->leftchild;
		else t->rightchild = tmp->leftchild;
		cur = tmp;
	}
	delete cur;
	cur = nullptr;
	return true;
}

5.完整代码实现

template<class K, class V>
struct BSTreeNode
{
	BSTreeNode(K key=K(),V val=V())
		:leftchild(nullptr)
		, rightchild(nullptr)
		, _key(key)
		, _value(val)
	{}
	BSTreeNode<K,V> * leftchild;
	BSTreeNode<K,V> * rightchild;
	K _key;
	V _value;
};
template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	bool Insert(const K& key, const V& value)
	{
		if (Find(key) == nullptr)
		{
			Node* newnode = new Node(key, value);
			if (_root == nullptr) _root = newnode;
			else
			{
				Node* pre = nullptr, *cur = _root;
				while (cur != nullptr)
				{
					pre = cur;
					if (key < cur->_key) cur = cur->leftchild;
					else cur = cur->rightchild;
				}
				if (key < pre->_key) pre->leftchild = newnode;
				else pre->rightchild = newnode;
			}
			return true;
		}
		return false;
	}
	Node* Find(const K& key)
	{
		if (_root == nullptr) return nullptr;
		Node* cur = _root;
		while (cur->_key != key)
		{
			if (key < cur->_key) cur = cur->leftchild;
			else if(key > cur->_key) cur = cur->rightchild;
			if (cur == nullptr) return nullptr;
		}
		return cur;
	}
	bool Erase(const K& key)
	{
		if (_root == nullptr) return false;
		//找到删除节点及其父节点
		Node* pre = nullptr, * cur = _root;
		while (cur->_key != key)
		{
			pre = cur;
			if (key < cur->_key) cur = cur->leftchild;
			else if (key > cur->_key) cur = cur->rightchild;
			if (cur == nullptr) return false;
		}
		//删除节点的子树<=1的情况
		if (cur->leftchild == nullptr || cur->rightchild == nullptr)
		{
			Node* tmp = cur->leftchild;
			if (cur->leftchild == nullptr) tmp = cur->rightchild;
			if (cur == _root) _root = tmp;
			else
			{
				if (cur->_key < pre->_key) pre->leftchild = tmp;
				else pre->rightchild = tmp;
			}
		}
		//删除节点的子树为2
		else
		{
			Node* tmp = cur->leftchild,*t=cur;
			while (tmp->rightchild != nullptr)
			{
				t = tmp;
				tmp = tmp->rightchild;
			}
			swap(tmp->_key, cur->_key);
			swap(tmp->_value, cur->_value);
			if (t->leftchild == tmp) t->leftchild = tmp->leftchild;
			else t->rightchild = tmp->leftchild;
			cur = tmp;
		}
		delete cur;
		cur = nullptr;
		return true;
	}
	
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr) return;
		_InOrder(root->leftchild);
		cout << root->_key << ":" << root->_value << " ";
		_InOrder(root->rightchild);
	}
private:
	Node* _root = nullptr;
};

6.二叉搜索树的应用

1.K模型:只使用键值key,根据key值是否存在解决相关问题。如查找某个单词是否存在的问题。

2.K-V模型:对于每一个key值都有一个value与之对应,根据二者的对应关系解决相关问题。如英文单词与中文的对应问题。

7.二叉搜索树的性能分析

如果二叉搜索树接近完全二叉树或者是完全二叉树,那么搜索的效率为O(logN)。

如果退化为单支树或者接近单支数,那么搜索的效率退化为O(N)。

为了解决这个问题,这里就需要学习红黑树或者AVL树了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值