【C++】二叉搜索树

目录

1.二叉搜索树的概念

1.1概念

1.2操作

2.二叉搜索树的实现

2.1基本框架

2.2中序遍历打印

2.3查找

2.3.1非递归

2.3.2递归

2.4插入

2.4.1非递归

2.4.2递归

2.5删除

2.5.1非递归

2.5.2递归

2.6完整代码

3.二叉搜索树的应用


1.二叉搜索树的概念

1.1概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
如下图所示

1.2操作

  1. 二叉搜索树的查找
    a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
    b、最多查找高度次,走到到空,还没找到,这个值不存在。
  2. 二叉搜索树的插入
    插入的具体过程如下:
    a. 树为空,则直接新增节点,赋值给root指针
    b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
    3.二叉搜索树的删除
    首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情
    况:
    a. 要删除的结点无孩子结点
    b. 要删除的结点只有左孩子结点
    c. 要删除的结点只有右孩子结点
    d. 要删除的结点有左、右孩子结点
    看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程
    如下:
    情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
    情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
    情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点
    中,再来处理该结点的删除问题--替换法删除

2.二叉搜索树的实现

2.1基本框架

cpp 复制代码
//树结点的数据结构
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;
		K _key;
		BSTreeNode(const K& key)
			:_key(key)
			,_left(nullptr)
			,_right(nullptr)
		{}
	};

	template<class K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;
    public:
//构造函数
		BSTree()
			:_root(nullptr)
		{}
//拷贝构造
		BSTree(const BSTree<K>& t)
		{
			_root = Copy(t._root);
		}
//赋值重载
		BSTree<K>& operator=(BSTree<K> t)
		{
			swap(_root, t._root);
			return *this;
		}
//析构函数
		~BSTree()
		{
			Destroy(_root);
			_root = nullptr;
		}

//各种数据操作,如插入,删除,查找

	private:
		void Destroy(Node* root)
		{
			if (root == nullptr)
				return;
			Destroy(root->_left);
			Destroy(root->_right);
			delete root;

		}
		Node* Copy(Node* root)
		{
			if (root == nullptr)
				return nullptr;
			Node* newRoot = new Node(root->_key);
			newRoot->_left = Copy(root->_left);
			newRoot->_right = Copy(root->_right);
			return newRoot;
		}

		Node* _root = nullptr;
	};

2.2中序遍历打印

cpp 复制代码
	
	    void InOrder()
		{
			_InOrder(_root);
		}

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

就是二叉树的中序遍历并打印,这里值得注意的是,这个功能用了两个函数来实现,是因为在类外面无法直接访问root,所以在类里面对这个功能套了一层外壳,使得即使不调用root也可以进行中序遍历打印。

2.3查找

2.3.1非递归

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

2.3.2递归

cpp 复制代码
		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}

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

2.4插入

2.4.1非递归

cpp 复制代码
		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* cur = _root;
			Node* parent=_root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			//找到空位了
			cur = new Node(key);
			
			if (parent->_key>key)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			return true;
		}

2.4.2递归

cpp 复制代码
		bool _InsertR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				//Find Insert Position
				//root==root->left or root->right
				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;
			}
		}

2.5删除

2.5.1非递归

cpp 复制代码
		bool Erase(const K& key)
		{
			if (_root == nullptr)
			{
				return false;
			}
			Node* cur = _root;
			Node* parent = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if(cur->_key>key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//找到了
					//1.左为空
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
							return true;
						}
						else
						{
							if (parent->_right == cur)
							{
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}

					}
					//2.右为空
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
					}
					//3.左右都不为空
					else
					{
						Node* parent = cur;
						Node* minRight = cur->_right;
						while (minRight->_left)
						{
							parent = minRight;
							minRight = minRight->_right;
						}
						cur->_key = minRight->_key;
						if (minRight == parent->_right)
						{
							parent->_right = minRight->_right;
						}
						else
						{
							parent->_left = minRight->_right;
						}
						delete minRight;
					}
					return true;
				}
			}
			delete cur;
			return false;
		}

2.5.2递归

cpp 复制代码
		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;
				//find erase position
				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(cur->_key, minRight->_key);
					return _EraseR(root->_right, key);
				}
				delete del;
				return true;
			}
		}

2.6完整代码

cpp 复制代码
#pragma once

namespace K
{
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;
		K _key;
		BSTreeNode(const K& key)
			:_key(key)
			,_left(nullptr)
			,_right(nullptr)
		{}
	};

	template<class K>
	class BSTree
	{
		typedef BSTreeNode<K> Node;
	public:
		BSTree()
			:_root(nullptr)
		{}
		
		BSTree(const BSTree<K>& t)
		{
			_root = Copy(t._root);
		}
		

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

		~BSTree()
		{
			Destroy(_root);
			_root = nullptr;
		}
		
		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* cur = _root;
			Node* parent=_root;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			//找到空位了
			cur = new Node(key);
			
			if (parent->_key>key)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			return true;
		}
		

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

		bool Erase(const K& key)
		{
			if (_root == nullptr)
			{
				return false;
			}
			Node* cur = _root;
			Node* parent = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if(cur->_key>key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//找到了
					//1.左为空
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
							return true;
						}
						else
						{
							if (parent->_right == cur)
							{
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}

					}
					//2.右为空
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
					}
					//3.左右都不为空
					else
					{
						Node* parent = cur;
						Node* minRight = cur->_right;
						while (minRight->_left)
						{
							parent = minRight;
							minRight = minRight->_right;
						}
						cur->_key = minRight->_key;
						if (minRight == parent->_right)
						{
							parent->_right = minRight->_right;
						}
						else
						{
							parent->_left = minRight->_right;
						}
						delete minRight;
					}
					return true;
				}
			}
			delete cur;
			return false;
		}
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

		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);
		}

	private:
		void Destroy(Node* root)
		{
			if (root == nullptr)
				return;
			Destroy(root->_left);
			Destroy(root->_right);
			delete root;

		}

		Node* Copy(Node* root)
		{
			if (root == nullptr)
				return nullptr;
			Node* newRoot = new Node(root->_key);
			newRoot->_left = Copy(root->_left);
			newRoot->_right = Copy(root->_right);
			return newRoot;
		}

		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;
				//find erase position
				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(cur->_key, minRight->_key);
					return _EraseR(root->_right, key);
				}
				delete del;
				return true;
			}
		}
		bool _InsertR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				//Find Insert Position
				//root==root->left or root->right
				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(Node* root, const K& key)
		{
			if (root == nullptr)
				return false;
			if (root->_key > key)
			{
				_FindR(root->_left, key);
			}
			else if (root->_key < key)
			{
				_FindR(root->_right, key);
			}
			else
			{
				return true;
			}
		}

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

		Node* _root = nullptr;
	};
}

3.二叉搜索树的应用

  1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
    的值
    比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
    以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
    在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
  2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方
    式在现实生活中非常常见:
    比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
    文单词与其对应的中文<word, chinese>就构成一种键值对;
    再比如统计单词次数 ,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
    现次数就是<word, count>就构成一种键值对
cpp 复制代码
// 改造二叉搜索树为KV结构
template<class K, class V>
struct BSTNode
 {
 BSTNode(const K& key = K(), const V& value = V())
   : _pLeft(nullptr) , _pRight(nullptr), _key(key), _Value(value)
 {}
 BSTNode<T>* _pLeft;
 BSTNode<T>* _pRight;
 K _key;
    V _value
 };
template<class K, class V>
class BSTree
 {
 typedef BSTNode<K, V> Node;
 typedef Node* PNode;
public:
 BSTree(): _pRoot(nullptr){}
 PNode Find(const K& key);
 bool Insert(const K& key, const V& value)
 bool Erase(const K& key)
private:
 PNode _pRoot;
 };
void TestBSTree3()
{
 // 输入单词,查找单词对应的中文翻译
 BSTree<string, string> dict;
 dict.Insert("string", "字符串");
 dict.Insert("tree", "树");
 dict.Insert("left", "左边、剩余");
 dict.Insert("right", "右边");
 dict.Insert("sort", "排序");
 // 插入词库中所有单词
 string str;
 while (cin>>str)
 {
 BSTreeNode<string, string>* ret = dict.Find(str);
 if (ret == nullptr)
 {
 cout << "单词拼写错误,词库中没有这个单词:" <<str <<endl;
 }
 else
 {
 cout << str << "中文翻译:" << ret->_value << endl;
 }
 }
}
void TestBSTree4()
{
 // 统计水果出现的次数
 string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", 
"苹果", "香蕉", "苹果", "香蕉" };
 BSTree<string, int> countTree;
 for (const auto& str : arr)
 {
 // 先查找水果在不在搜索树中
 // 1、不在,说明水果第一次出现,则插入<水果, 1>
 // 2、在,则查找到的节点中水果对应的次数++
 //BSTreeNode<string, int>* ret = countTree.Find(str);
 auto ret = countTree.Find(str);
 if (ret == NULL)
 {
 countTree.Insert(str, 1);
 }
 else
 {
 ret->_value++;
 }
 }
 countTree.InOrder();
}
相关推荐
睡不着还睡不醒1 小时前
【数据结构强化】应用题打卡
算法
sp_fyf_20241 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-05
人工智能·深度学习·神经网络·算法·机器学习·语言模型·自然语言处理
C++忠实粉丝2 小时前
前缀和(6)_和可被k整除的子数组_蓝桥杯
算法
木向2 小时前
leetcode42:接雨水
开发语言·c++·算法·leetcode
TU^2 小时前
C语言习题~day16
c语言·前端·算法
吃什么芹菜卷2 小时前
深度学习:词嵌入embedding和Word2Vec
人工智能·算法·机器学习
wclass-zhengge2 小时前
数据结构与算法篇(树 - 常见术语)
数据结构·算法
labuladuo5202 小时前
AtCoder Beginner Contest 372 F题(dp)
c++·算法·动态规划
夜雨翦春韭2 小时前
【代码随想录Day31】贪心算法Part05
java·数据结构·算法·leetcode·贪心算法
hsling松子7 小时前
使用PaddleHub智能生成,献上浓情国庆福
人工智能·算法·机器学习·语言模型·paddlepaddle