二叉搜索树

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;
	};
}
相关推荐
Jerry1 小时前
LeetCode 28. 找出字符串中第一个匹配项的下标
算法
Jerry3 小时前
LeetCode 459. 重复的子字符串
算法
哥不想学算法5 小时前
【C++】字符串字面量拼接
开发语言·c++
海石5 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石5 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry7 小时前
LeetCode 151. 反转字符串中的单词
算法
gugucoding8 小时前
31. 【C语言】堆栈与队列的实现
c语言·开发语言·数据结构·链表
ChaoZiLL9 小时前
我的数据结构3——链表(link list)
数据结构·链表
a11177610 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜10 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai