序文
二分探索木の本質も二分木ですが、その特殊なデータ ストレージにより
左子树的值都更小,右子树的值都更大
、ほとんどの場合、查找更为高效
. このブログでは 2 つの二分探索木について説明する应用搜索的场景
ので、早速、今日の学習を始めましょう。
記事ディレクトリ
1. キーモデル
キーのモデルの本質は実際には在不在
問題です
- 例:
门禁系统
. 学校に出入りするときは、キャンパスカードをスワイプする必要があるかもしれません. 実際、キャンパスカードにはチップがあります. アクセスコントロールとやり取りした後、あなたの情報を読み取ることができます. それを見つけたら, あなたは許可されます.見つからない場合は通過できません拿着这个信息,去数据库中查找
通過は許可されています。- 別の例:
车库系统
. このガレージに駐車スペースがある場合、ガレージに出入りするときに、ガレージのポールが直接持ち上げられて出入りします。ただし、駐車スペースがない場合でも、駐車場に入るときにポールを上げることができますが、出るときにポールを上げるには駐車料金を支払う必要があります。これには、情報の検索と照合も含まれます。
最普通的二叉搜索树就是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+回车可以正常结束这样的循环
結論
読んでくれてありがとう
この記事が役立つと思われる場合は、ブロガーをサポートすることをお勧めします。これは私にとって非常に重要です.