【C++】二分探索木の応用

序文

二分探索木の本質も二分木ですが、その特殊なデータ ストレージにより左子树的值都更小,右子树的值都更大、ほとんどの場合、查找更为高效. このブログでは 2 つの二分探索木について説明する应用搜索的场景
ので、早速、今日の学習を始めましょう。

ここに画像の説明を挿入

1. キーモデル

キーのモデルの本質は実際には在不在問題です

  1. 例: 门禁系统. 学校に出入りするときは、キャンパスカードをスワイプする必要があるかもしれません. 実際、キャンパスカードにはチップがあります. アクセスコントロールとやり取りした後、あなたの情報を読み取ることができます. それを見つけたら, あなたは許可されます.見つからない場合は通過できません拿着这个信息,去数据库中查找通過は許可されています。
  2. 別の例: 车库系统. このガレージに駐車スペースがある場合、ガレージに出入りするときに、ガレージのポールが直接持ち上げられて出入りします。ただし、駐車スペースがない場合でも、駐車場に入るときにポールを上げることができますが、出るときにポールを上げるには駐車料金を支払う必要があります。これには、情報の検索と照合も含まれます。

最普通的二叉搜索树就是Key的模型每个节点只存储一个数据、およびその实际意义就是那个数据的意义
たとえば、次の二分探索木では、その意味は整数の格納です。
ここに画像の説明を挿入
二分探索木キーモデルのコードは[C++]二分探索木にあります

2. Key_Value のモデル

Key_Value のモデルは実際には映射关系,ですKey不再单纯只有Key的意义,其还对应了一个Value
鍵と同じように、鍵であるだけでなく、対応するロックでもあります。学籍番号は単なる数字の羅列ではなく、学生情報管理システムの情報とも対応しています。

たとえば、中英翻訳辞書を完成させたい場合、Key_Value モデルを使用できます.入力する Key は「sort」で、対応する Value は「sort」です。

二分探索木の Key_Value モデルのコードを簡単に書きましょう。実際には基本框架和Key模型一样、クラス テンプレート パラメータにもう 1 つのテンプレート パラメータがあるだけです。

//类模板,用于存储不同数据
	template<class K,class V>
	struct BinarySearchTree
	{
    
    
		BinarySearchTree<K,V>*_left;//左子树
		BinarySearchTree<K,V>*_right;//右子树
		K _key;//key值
		V _value;//value值

		//构造函数
		BinarySearchTree(const K&key,const V&value)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			,_value(value)
		{
    
    }

		~BinarySearchTree()
		{
    
    
			_left = nullptr;
			_right = nullptr;
		}
	};


	template<class K,class V>
	class BSTree
	{
    
    
		typedef BinarySearchTree<K,V> Node;
	public:
		//析构
		~BSTree()
		{
    
    
			Destroy(_root);
			_root = nullptr;
		}

		//插入
		bool Insert(const K&key,const V&value)
		{
    
    
			//头为空时单独处理
			if (_root == nullptr)
			{
    
    
				_root = new Node(key,value);
				return true;
			}

			//循环找插入的位置
			Node*cur = _root;
			//记录父节点,实现链接
			Node*parent = nullptr;
			while (cur)
			{
    
    
				parent = cur;
				if (key > cur->_key)
					cur = cur->_right;
				else if (key < cur->_key)
					cur = cur->_left;
				else
					//相等则返回假
					return false;
			}

			//找到了要插入的位置
			cur = new Node(key,value);
			//链接
			if (key > parent->_key)
				parent->_right = cur;
			else
				parent->_left = cur;

			return true;
		}

		//中序遍历
		//因为二叉搜索树的特点,中序打印出来就是升序

		//实现封装
		void InOrder()
		{
    
    
			_InOrder(_root);
			cout << endl;
		}

		//查找
		Node* Find(const K&key)
		{
    
    
			Node*cur = _root;
			//循环查找
			while (cur)
			{
    
    
				//比当前值小,则往左走
				if (key < cur->_key)
					cur = cur->_left;
				else if (key > cur->_key)//大则往右走
					cur = cur->_right;
				else//不然就是相等,相等就是找到了
					return cur;
			}

			//循环没返回说明没查到
			return nullptr;
		}

		//删除
		bool Erase(const K&key)
		{
    
    
			//分成两类
			//左或者右为空(包括叶子结点)
			//左右孩子都有

			//首先先找节点
			Node*cur = _root;
			//记录父亲节点
			Node*parent = nullptr;

			while (cur)
			{
    
    
				//parent = cur;
				if (key < cur->_key)
				{
    
    
					parent = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
    
    
					parent = cur;
					cur = cur->_right;
				}
				else
				{
    
    
					//找到了

					//分两种情况

					//左为空
					if (cur->_left == nullptr)
					{
    
    
						//还有可能删到根节点的一边为空(有点像歪脖子树)
						if (cur == _root)
							_root = cur->_right;
						else
						{
    
    
							//要判断父节点链接左还右
							if (parent->_left == cur)
								parent->_left = cur->_right;
							else
								parent->_right = cur->_right;
						}

						delete cur;
						cur = nullptr;

						return true;

					}  // 右为空
					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;
						}

						delete cur;
						cur = nullptr;

						return true;
					}
					else
					{
    
    
						//左右子树都不为空
						//找保姆
						//左子树的最大节点 or 右子树的最小节点  二者都可以
						//  最右节点             最左节点

						Node*pMinRight = cur;//右子树的最小节点的父节点
						Node*MinRight = cur->_right;//右子树的最小节点

						while (MinRight->_left)
						{
    
    
							pMinRight = MinRight;
							MinRight = MinRight->_left;
						}

						//直接赋值
						cur->_key = MinRight->_key;

						//判断父节点要链接左还是右
						if (pMinRight->_left == MinRight)
							pMinRight->_left = MinRight->_right;
						else
							pMinRight->_right = MinRight->_right;

						//删除MinRight,因为完成交换了
						delete MinRight;
						MinRight = nullptr;

						return true;
					}
				}
			}

			return false;
		}

	protected:

		//销毁二叉搜索树
		void Destroy(Node*root)
		{
    
    
			if (root == NULL)
				return;

			//先删除左右节点,再删除当前节点
			Destroy(root->_left);
			Destroy(root->_right);

			delete root;
			root = nullptr;
		}

		//因为要递归,所以要单独编写
		//注意此处不可以加缺省值_root,因为缺省值需要是常量
		void _InOrder(Node*root)
		{
    
    
			if (root == nullptr)
				return;

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

	private:
		Node*_root = nullptr;//根节点
	};

テストする例として、英中翻訳を見てみましょう
ここに画像の説明を挿入
ctrl+z+回车可以正常结束这样的循环

結論

読んでくれてありがとう

この記事が役立つと思われる場合は、ブロガーをサポートすることをお勧めします。これは私にとって非常に重要です.
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/m0_72563041/article/details/130264731