【 二叉搜索树(BinarySearchTree)】

一、非递归实现

1.二叉搜索树的概念

二叉搜索树又称二叉排序树,它可以是一颗空树,若不是空树,则它具有以下性质:

a.若它的左子树不为空,则左子树上的所有的值都小于根节点的值
b.若它的右子树不为空,则右子树上的所有的值都大于根节点的值
c.它的左右子树也分别为二叉搜索树,而且不存在数据冗余

判断下面三棵树是否为二叉搜索树
在这里插入图片描述

A树中以值为10的节点,它的右子树不满足二叉搜索树的概念,它右子树上的值小于根节点的值,所以不是二叉搜索树。
B、C两树都满足二叉搜索树的概念,所以它两都是二叉搜索树

2.非递归实现(Insert 、Erase)

维护一颗二叉树要对它的节点进行管理,下面是对节点的描述:

//BinarySearchTreeNode
template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;

	BSTreeNode(const K& key)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

🌞二叉搜索树的插入:

1、当二叉搜索树是一颗空树时,直接把节点赋值给_root;
2、当二叉搜索树是非空树时:
a、当插入的值比当前节点的值时,需要去当前节点的左子树去找合适的位子;
b、当插入的值比当前节点的值时,需要去当前节点的右子树去找合适的位子;
c、重复a、b过程,直到找到合适的位子(过程中如果有冗余(一样)的值,插入失败)

如下为插入16和32的图像过程:
在这里插入图片描述

代码实现:

typedef BSTreeNode<K> Node;
bool Insert(const K& key)
{
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}
	
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if(cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}
	
	if (parent->_key < key)
	{
		parent->_right = new Node(key);
	}
	else
	{
		parent->_left = new Node(key);
	}
	
	return true;
}

🌞二叉搜索树的查找:

1、如果是空树,返回false,没有找到;
2、如果是非空树,进行查找:
a、如果查找的值比_root的值时,在其左子树进行查找;
b、如果查找的值比_root的值时,再其右子树进行查找;
c、重复a、b操作,直到找到为止(返回true)或没有找到为止(返回false);

假设查找33,把_root 赋值给 cur,因为33比cur节点的值,所以要去右子树去找,cur=cur->_right;然后33又比当前cur的值,所以要去它的左子树去找,cur=cur->_left;此时,33和当前cur的值相等,所以找到了,返回true
在这里插入图片描述

typedef BSTreeNode<K> Node;
bool Find(const K& key)
	{
		if (_root == nullptr)
		{
			return false;
		}
		
		Node* cur = _root;
		while(cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}
		}
		
		return false;
	}

最后分析一下二叉搜索树查找的时间复杂度:
最坏的情况下,二叉搜索树可能是一条单边树,如下图:
在这里插入图片描述
对这颗二叉搜索树进行查找,需要找高度次,所以时间复杂度为O(N)

🌞二叉搜索树的删除:

根据要删除的节点有几个孩子,把删除节点的方式分为两种:
1、直接删除:没有孩子或者只有一个孩子,直接删除该节点,把它的孩子托管给它的父亲;
a、被删除节点没有孩子;
b、被删除节点只有左孩子;
c、被删除节点只有右孩子;
其实可以把 a 归类于 b或者c中,假设把 a 归类于 b 中,它的孩子是一个空孩子;
图形演示(假设已经找到了要删除的节点):
假设要删除的节点为7,该节点没有孩子,但我们把它归类为b中,为了方便理解,把它的左孩子(空)画了出来,因为要删除节点是其父亲的右孩子,所以把要删除节点的左孩子链接给其父亲的右孩子。(可以试着删除一下14节点,画一下图像演示)
在这里插入图片描述
假设要删除的节点是根节点,它属于只有一个孩子或者没有孩子,此时的parent为nullptr,不能进行托管,那么我们判断一下cur 是否等于 _root,如果等于,直接把_root的非空孩子给_root;
在这里插入图片描述

代码实现(直接删除法):

if(cur->_left == nullptr)//只有右孩子
{
	if(cur == _root)
	{
		_root = cur->_right;
	}
	else
	{
		if(cur == parent->_left)
		{
			parent->_left = cur->_right;
		}
		else 
		{
			parent->_right = cur->_right;
		}
	}
	delete cur;
}
else if(cur->_right == nullptr)没有孩子或者只有左孩子
{
	if(cur == nullptr)
	{
		_root = cur->_left;
	}
	else
	{
		if(cur == parent->_left)
		{
			parent->_left = cur->_left;
		}
		else
		{
			parent->_right = cur->_left;
		}
	}
	delete cur;
}

2、替代法删除:
a、要删除的节点有两个孩子,因为无法将两个孩子同时都托管给其父亲,所以我们需要找一个节点来替代它。那么可以找谁可以来替代它呢?可以找其左子树的最大节点,因该节点比左子树的所有值都大,比右子树的所有值都小;还可以找右子树的最小节点,因为该节点比右子树所有节点都小,比左子树的所有节点都大。(以找右子树的最小节点为例)
假设删除节点3,在右子树中找最小节点,最小节点的左子树为空;找到最小节点后把它与被删除节点交换,然删除Minright就可以了(直接删除法,没有左孩子,可能有右孩子,把有孩子托管给其父亲,即使右孩子为空)
在这里插入图片描述
代码实现(替代删除法):

else//左右孩子都不为空
{
	Node* minParent = cur;
	Node* Minright = cur->_right;
	
	while(Minright->_left)
	{
		minParent = Minright;
		Minright = Minright->_left;
	}
	
	swap(cur->_key,Minright->_key);
	//因为右边找最小,所以找到的一定左子树为空
	if(Minright == minParent->_left)
	{
		minParent->_left = Minright->_right;
	}
	else
	{
		minParent->_right = Minright->_right;
	}
	delete minRight;	

删除的整体代码实现:

typedef BSTressNode<K> Node;
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->_right;
		}
		else if(cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else//找到要删除的值了
		{
			if(cur->_left == nullptr)
			{
				if(cur == _root)
				{
					_root = cur->_right;
				}
				else
				{
					if(cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else 
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
			}
			else if(cur->_right == nullptr)
			{
				if(cur == nullptr)
				{
					_root = cur->_left;
				}
				else
				{
					if(cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
			}
			else//左右孩子都不为空
			{
				Node* minParent = cur;
				Node* Minright = cur->_right;
				
				while(Minright->_left)
				{
					minParent = Minright;
					Minright = Minright->_left;
				}
				
				swap(cur->_key,Minright->_key);
				//因为右边找最小,所以找到的一定左子树为空
				if(Minright == minParent->_left)
				{
					minParent->_left = Minright->_right;
				}
				else
				{
					minParent->_right = Minright->_right;
				}
				delete MinRight;	
			}
			
			return true;
		}
	}

	return false;	
}

3、非递归实现二叉搜索树完整代码

1、代码实现

#include<iostream>
using namespace std;

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

	BSTreeNode(const K& key)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
	Node* CopyTree(Node* root)
	{
		if(root == nullptr)
		{
			return nullptr;
		}

		Node* CopyNode = new Node(root->_key);
		CopyNode->_left = CopyTree(root->_left);
		CopyNode->_right = CopyTree(root->_right);

		return CopyNode;
	}

	void DestroyTree(Node* root)
	{
		if(root == nullptr)
		{
			return;
		}
			
		DestroyTree(root->_left);
		DestroyTree(root->_right);
		delete root;
	}
public:
	BSTree() = default;//c++11中强制编译器自己生成默认构造函数

	BSTree(const BSTree<K>& t)
	{
		_root = CopyTree(t._root);
	}
	
	~BSTree()
	{
		DestroyTree(_root);
		_root = nullptr;
	}

	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(_root,t._root);
		return *this;
	}
	
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if(cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		
		if (parent->_key < key)
		{
			parent->_right = new Node(key);
		}
		else
		{
			parent->_left = new Node(key);
		}
		
		return true;
	}

	bool Find(const K& key)
	{
		if (_root == nullptr)
		{
			return false;
		}
		
		Node* cur = _root;
		while(cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}
		}
		
		return false;
	}

	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->_right;
			}
			else if(cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else//找到要删除的值了
			{
				if(cur->_left == nullptr)//只有右孩子
				{
					if(cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if(cur == parent->_left)
						{
							parent->_left = cur->_right;
						}
						else 
						{
							parent->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if(cur->_right == nullptr)//没有孩子或者只有左孩子
				{
					if(cur == nullptr)
					{
						_root = cur->_left;
					}
					else
					{
						if(cur == parent->_left)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
					delete cur;
				}
				else//左右孩子都不为空
				{
					Node* minParent = cur;
					Node* Minright = cur->_right;
					
					while(Minright->_left)
					{
						minParent = Minright;
						Minright = Minright->_left;
					}
					
					swap(cur->_key,Minright->_key);
					//因为右边找最小,所以找到的一定左子树为空
					if(Minright == minParent->_left)
					{
						minParent->_left = Minright->_right;
					}
					else
					{
						minParent->_right = Minright->_right;
					}
					delete MinRight;	
				}
				
				return true;
			}
		}
	
		return false;	
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
private:
	Node* _root = nullptr;
};

二、递归实现

1、递归实现(Insert、Erase)

🌞二叉搜索树递归插入:

1、如果是一颗空树,因为是引用传参,root就是_root的别名,new一个Node给root也就是给_root;
2、如果是一颗非空树,引用传参;
a、当root->_key < key时,就递归插入它的右子树_EraseR(root->_right,key);
b、当root->_key > key时,就递归插入它的左子树_EraseR(root->_left,key);
c、当root->_key == key时,插入失败,return false;

图形演示:

在这里插入图片描述
代码实现:

bool InsertR(const K& key)
{
	return _InsertR(_root,key);
}

bool _InsertR(Node*& root,const K& key)
{
	if(root == nullptr)
	{
		root = new Node(key);
		return true;
	}

	if(root->_key < key)
	{
		_InsertR(root->_right,key);
	}
	else if(root->_key > key)
	{
		_InsertR(root->_left,key);
	}
	else 
	{
		return false;
	}
}

🌞二叉搜索树递归查找:

bool FindR(const K& key)
{
	return _FindR(_root,key);
}

bool _FindR(const Node* root,const K& key)
{
	if(root == nullptr)
	{
		return false;
	}
	
	if(root->_key < key)
	{
		_FindR(root->_right,key);
	}
	else if(root->_key > key)
	{
		_FindR(root->_left,key);
	}
	else
	{
		return true;
	}
}

🌞二叉搜索树删除递归:

1、如果是一颗空树,删除失败,返回false;
2、如果是一颗非空树,引用传参;
a、当root->_key < key时,就递归删除它的右子树_EraseR(root->_right,key);
b、当root->_key > key时,就递归删除它的左子树_EraseR(root->_left,key);
c、当root->_key == key时,就可以进行删除了,删除方法和非递归删除一样;
因为是引用传参,所以root 就是 其父亲的一个孩子,如图假设是左孩子(要删除节点):
在这里插入图片描述
我们要把7的左孩子托管给被删除节点的父亲,也就是10->_left,因为root是10->_left 的别名,于是有root = root->_left(10->_left = root->_left),把没有孩子的节点归于只有左孩子;

代码实现:

bool EraseR(const K& key)
{
	return _EraseR(_root,key);
}
bool _EraseR(Node*& root,const K& key)
{
	if(root == nullptr)
	{
		return false;
	}
	
	if(root->_key < key)
	{
		_EraseR(root->_right,key);
	}
	else if(root->_key > key)
	{
		_EraseR(root->_left,key);
	}
	else
	{
		Node* del = root;
		if(root->_left == nullptr)
		{
			root = root->_right;
		}
		else if(root->_right == nullptr)
		{
			root = root->_left;
		}
		else
		{
			Node* Minright = root->_right;
			while(Minright->_left)
			{
				Minright = Minright->_left;
			}

			swap(root->_key,Minright->_key);
			return _EraseR(root->_right,key);
		}
		delete del;
		return true;
	}
}

2、 二叉搜索树递归完整代码

#include<iostream>
using namespace std;

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

	BSTreeNode(const K& key)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
	Node* CopyTree(Node* root)
	{
		if(root == nullptr)
		{
			return nullptr;
		}

		Node* CopyNode = new Node(root->_key);
		CopyNode->_left = CopyTree(root->_left);
		CopyNode->_right = CopyTree(root->_right);

		return CopyNode;
	}

	void DestroyTree(Node* root)
	{
		if(root == nullptr)
		{
			return;
		}
			
		DestroyTree(root->_left);
		DestroyTree(root->_right);
		delete root;
	}
public:
	BSTree() = default;//c++11中强制编译器自己生成默认构造函数

	BSTree(const BSTree<K>& t)
	{
		_root = CopyTree(t._root);
	}
	
	~BSTree()
	{
		DestroyTree(_root);
		_root = nullptr;
	}

	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(_root,t._root);
		return *this;
	}
	
	bool InsertR(const K& key)
	{
		return _InsertR(_root,key);
	}



	bool FindR(const K& key)
	{
		return _FindR(_root,key);
	}



	bool EraseR(const K& key)
	{
		return _EraseR(_root,key);
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	bool _FindR(const Node* root,const K& key)
	{
		if(root == nullptr)
		{
			return false;
		}
		
		if(root->_key < key)
		{
			_FindR(root->_right,key);
		}
		else if(root->_key > key)
		{
			_FindR(root->_left,key);
		}
		else
		{
			return true;
		}
	}
	
	bool _InsertR(Node*& root,const K& key)
	{
		if(root == nullptr)
		{
			//这里需要做解释
			root = new Node(key);
			return true;
		}
	
		if(root->_key < key)
		{
			_InsertR(root->_right,key);
		}
		else if(root->_key > key)
		{
			_InsertR(root->_left,key);
		}
		else 
		{
			return false;
		}
	}
	
	bool _EraseR(Node*& root,const K& key)
	{
		if(root == nullptr)
		{
			return false;
		}
		
		if(root->_key < key)
		{
			_EraseR(root->_right,key);
		}
		else if(root->_key > key)
		{
			_EraseR(root->_left,key);
		}
		else
		{
			Node* del = root;
			if(root->_left == nullptr)
			{
				root = root->_right;
			}
			else if(root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				Node* Minright = root->_right;
				while(Minright->_left)
				{
					Minright = Minright->_left;
				}
	
				swap(root->_key,Minright->_key);
				return _EraseR(root->_right,key);
			}
			delete del;
			return true;
		}
	}
	
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
private:
	Node* _root = nullptr;
};

三、二叉搜索树的应用

1、K模型:

K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。(我们上面写的就是K模型)
比如:
🌞给一个单词word,判断该单词是否拼写正确,具体方式如下:
以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
🌞在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2、 KV模型:

每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:
比如:
🌞英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
🌞再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

KV模型的节点描述:

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

	BSTreeNode(const K& key,const V& value)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
		,_value(value)
		{}
};

可以自行实现KV模型的二叉搜索树,方法和K模型一样。

  • 17
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

桑榆非晚ᴷ

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

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

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

打赏作者

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

抵扣说明:

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

余额充值