二叉搜索树

1.关联式容器

根据"数据在容器中的排列"特性,容器分为序列式和关联式两种,前面我们已经讨论过序列式容器了,后面一段时间我们将讨论关联式容器。

标准的STL关联式容器分为 set(集合)和 map(映射表)两大类,以及这两大类的衍生体multiset(多键集合)和 multimap(多键映射表)。这些容器的底层机制均以 RB-tree(红黑树)完成。RB-tree 也是一个独立的容器,但并不开放给外界使用 ...

所谓的关联式容器,观念上类似关联式数据库:每笔数据(每个元素)都有一个键值(key)和实值(value)。当元素被插入到关联式容器中时,容器内部结构变异找其键值大小,以某种特定规则就按这个元素放置于适当位置。关联式容器没有所谓头尾(中有==只有最大元素和最小元素),所以不会有所谓push_back()、push_front()、pop_back()、pop_front()、begin()、end() 这样的行为操作。
一般情况下,关联式容器的内部结构是一个平衡二叉树,一以便获得良好的搜寻效率。

2.二叉搜索树

(1) 概念

(2) 二叉搜索树的性能分析

(3) 二叉搜索树的插入

1. 树为空,则直接新增结点,赋值给root指针
2. 树不空,按二叉搜索树性质, 插入值比当前结点大往走,插入值比当前结点小往左走,找到空位置,插入新结点。
3. 如果支持插入相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,找到空位置,插入 新结点。(要注意的是要保持逻辑一致性,插入相等的值不要一会往右走,一会往左走)

(4) 二叉搜索树的查找

(5) 二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回false。
如果查找元素存在则分以下四种情况分别处理:(假设要删除的结点为N)
1. 要删除结点N左右孩子均为空
2. 要删除的结点N左孩子位空,右孩子结点不为空
3. 要删除的结点N右孩子位空,左孩子结点不为空
4. 要删除的结点N左右孩子结点均不为空
对应以上四种情况的解决方案:
1. 把N结点的父亲对应孩子指针指向空,直接删除N结点(情况1可以当成2或者3处理,效果是一样 的)
2. 把N结点的父亲对应孩子指针指向N的右孩子,直接删除N结点
3. 把N结点的父亲对应孩子指针指向N的左孩子,直接删除N结点

4. 无法直接删除N结点,因为N的两个孩子无处安放,只能用替换法删除。找N左子树的值最大结点R(最右结点)或者N右子树的值最小结点R(最左结点)替代N,因为这两个结点中任意一个,放到N的位置,都满足二叉搜索树的规则。替代N的意思就是N和R的两个结点的值交换,转而变成删除R结点,R结点符合情况2或情况3,可以直接删除。

(6) 二叉搜索树的代码实现

cpp 复制代码
namespace zyt
{
	// 节点
	template <class K>
	struct BSNode
	{
		K _key;
		BSNode<K>* _left;
		BSNode<K>* _right;

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

	template <class K>
	struct BSTree
	{
		typedef BSNode<K> Node;
	public:
		bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* parent = _root;
			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;
				}
			}
			cur = new Node(key);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else if (parent->_key > key)
			{
				parent->_left = cur;
			}
			return true;
		}

		bool find(const K& key)
		{
			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)
		{
			// 先找到要删除的位置
			Node* parent = _root;
			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 // 找到要删除的位置
				{	// 情况1
					if (cur->_left == nullptr && cur->_right == nullptr)
					{
						if (parent->_left == cur)
							parent->_left = nullptr;
						if (parent->_right == cur)
							parent->_right = nullptr;

						delete cur;
						return true;
					}
					else if (cur->_left == nullptr )
					{
						if (parent == _root)
						{// 删除根节点的情况
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
								parent->_left = cur->_right;
							else
								parent->_right = cur->_right;

							delete cur;
							return true;
						}
					}
					else if (cur->_right == nullptr)
					{
						if (parent == _root)
						{// 删除根节点的情况
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
								parent->_left = cur->_left;
							else
								parent->_right = cur->_left;

							delete cur;
							return true;
						}
					}
					else
					{ // 新指针寻找左子树最大值
						Node* newparent = cur;
						Node* newcur = cur;
						
						while (newcur->_right)// 找到左子树的最右节点
						{
							newparent = newcur;
							newcur = newcur->_right;
						}
						cur->_key = newcur->_key;
						
						if (newparent->_left == newcur)
							newparent->_left = nullptr;
						else
							newparent->_right = nullptr;

						delete newcur;
						return true;
					}

				}
			}
			return false;
		}

		void InOrder()
		{ // 封装一个函数方便外界调用
			_InOreder(_root);
			cout << endl;
		}

	private:
		void _InOreder(Node* root)
		{
			if (root == nullptr)
				return;

			_InOreder(root->_left);
			cout << root->_key << " ";
			_InOreder(root->_right);
		}

		Node* _root = nullptr;
	};
}

3.key 和 key/value 使用

(1) key 使用场景

只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断key在不在。key的搜索场景实现的二叉树搜索树支持增删查,但是不支持修改,修改key破坏搜索树结构了。
场景:检查一篇英文文章单词拼写是否正确,将词库中所有单词放入二叉搜索树,读取文章中的单 词,查找是否在二叉搜索树中,不在则波浪线标红提示。

(2) key / value 使用场景

每一个关键码key,都有与之对应的值value,value可以任意类型对象。树的结构中(结点)除了需要存 储key还要存储对应的value,增/删/查还是以key为关键字走⼆叉搜索树的规则进行比较,可以快速查 找到key对应的value。key/value的搜索场景实现的二叉树搜索树支持修改,但是不支持修改key,修 改key破坏搜索树结构了,可以修改value。
场景:简单中英互译字典,树的结构中(结点)存储key(英文)和vlaue(中文),搜索时输入英文,则同时 查找到了英文对应的中文。

(3) key/value二叉搜索树代码实现

cpp 复制代码
namespace zyt1
{
	// 节点
	template <class K ,class V>
	struct BSNode
	{
		K _key;
		V _value;
		BSNode<K,V>* _left;
		BSNode<K,V>* _right;

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

	template <class K,class V>
	struct BSTree
	{
		typedef BSNode<K,V> Node;
	public:
		bool Insert(const K& key, const V& value)
		{
			if (_root == nullptr)
			{
				_root = new Node(key,value);
				return true;
			}
			Node* parent = _root;
			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;
				}
			}
			cur = new Node(key,value);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else if (parent->_key > key)
			{
				parent->_left = cur;
			}
			return true;
		}

		Node* find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}
		bool erase(const K& key)
		{
			// 先找到要删除的位置
			Node* parent = _root;
			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 // 找到要删除的位置
				{	// 情况1
					if (cur->_left == nullptr && cur->_right == nullptr)
					{
						if (parent->_left == cur)
							parent->_left = nullptr;
						if (parent->_right == cur)
							parent->_right = nullptr;

						delete cur;
						return true;
					}
					else if (cur->_left == nullptr)
					{
						if (parent == _root)
						{// 删除根节点的情况
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
								parent->_left = cur->_right;
							else
								parent->_right = cur->_right;

							delete cur;
							return true;
						}
					}
					else if (cur->_right == nullptr)
					{
						if (parent == _root)
						{// 删除根节点的情况
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
								parent->_left = cur->_left;
							else
								parent->_right = cur->_left;

							delete cur;
							return true;
						}
					}
					else
					{ // 新指针寻找左子树最大值
						Node* newparent = cur;
						Node* newcur = cur;

						while (newcur->_right)// 找到左子树的最右节点
						{
							newparent = newcur;
							newcur = newcur->_right;
						}
						cur->_key = newcur->_key;

						if (newparent->_left == newcur)
							newparent->_left = nullptr;
						else
							newparent->_right = nullptr;

						delete newcur;
						return true;
					}

				}
			}
			return false;
		}

		BSTree() = default;// 强制生成
		// 拷贝
		BSTree(const 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);
			_root = nullptr;
		}
		
		void Destory(Node* root)
		{
			if (root == nullptr)
				return;
			Destory(root->_left);
			Destory(root->_right);
			delete root;
		}

		// 构造
		Node* Copy(Node* root)
		{
			if (root == nullptr)
				return nullptr;
			Node* newnode = new Node(root->_key, root->_value);
			newnode->_left = Copy(root->_left);
			newnode->_right = Copy(root->_right);
			return newnode;
		}
		
		void InOrder()
		{ // 封装一个函数方便外界调用
			_InOreder(_root);
			cout << endl;
		}

	private:
		void _InOreder(Node* root)
		{
			if (root == nullptr)
				return;

			_InOreder(root->_left);
			cout << root->_key << ":" << root->_value << endl;;
			_InOreder(root->_right);
		}

		Node* _root = nullptr;
	};
}
相关推荐
CYBEREXP200818 分钟前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
ZSYP-S37 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos40 分钟前
c++------------------函数
开发语言·c++
yuanbenshidiaos44 分钟前
C++----------函数的调用机制
java·c++·算法
唐叔在学习1 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA1 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
tianmu_sama1 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
chengooooooo1 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc1 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
羚羊角uou1 小时前
【C++】优先级队列以及仿函数
开发语言·c++