🌻个人主页:路飞雪吖~
✨专栏:C/C++
目录
一、二叉搜索树(K模型)的模拟实现
🌟二叉搜索树的概念
🌟二叉搜索树的操作
🌠二叉搜索树的查找
🌠二叉搜索树的插入
🌠二叉搜索树的删除
🌠二叉搜索树的中序遍历
二、二叉搜索树(KV模型)的模拟实现
三、二叉搜索树的应用场景
四、二叉搜索树的性能分析
五、模拟实现的代码
一、二叉搜索树(K模型)的模拟实现
🌟二叉搜索树的概念
二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树。
一颗二叉搜索树,可以为空;
如果不为空,满足以下性质:
• 非空左子树的所有键值小于其根节点的键值
• 非空右子树的所有键值大于其根节点的键值
• 左、右子树都是二叉搜索树
🌟二叉搜索树的操作
K模型:即只有key作为关键码,结构中只需存储key即可,关键码即为需要搜索到的值。
模拟实现的基本框架:
#pragma once
#include<iostream>
using namespace std;
template<class K>
struct BSTNode
{
K _key;
BSTNode<K>* _left;
BSTNode<K>* _right;
BSTNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{}
};
template<class K>
class BSTree
{
typedef BSTNode<K> Node;
public:
private:
Node* _root = nullptr;
};
• BSTNode 搜索二叉树的节点不仅要存该节点的值,还要存左右节点的指针,因此把节点封装成一个节点类,以便对节点插入、删除、查找等进行操作。
• K _key; 节点的值
BSTNode<K>* _left; 节点的左
BSTNode<K>* _right; 节点的右• BSTNode(const K& key) 节点的初始化
• BSTree 二叉搜索树的主体实现(查找、删除、插入)
• typedef BSTNode<K> Node; 对节点进行取别名
🌠二叉搜索树的查找
我们知道在搜索树二叉树中,左子树的节点小于根节点的值,根节点的值小于右子树的值,因此我们根据这个特性对搜索二叉树进行查找:
//查找
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 Insert(const K& key)
{
//1、树为空,直接新增节点,赋值给root指针
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
}
• 树为空,直接新增节点,赋值给root指针;
//树不为空,按二叉搜索树性质查找插入位置,插入新节点
Node* parent = nullptr;//记录插入节点的前一个指针,方便插入时操作
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;//插入已有的值返回false
}
}
• 树不为空,按二叉搜索树性质查找插入的位置 ;
• Node* parent = nullptr; 记录插入节点的前一个指针,方便插入时的操作;
• 插入已有的值返回false,二叉搜索树中不能存相同的值。
//找到位置,插入新节点
cur = new Node(key);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
• 找到位置,创建节点,并判断节点是插入在前一个节点的左边还是右边。
//插入
bool Insert(const K& key)
{
//1、树为空,直接新增节点,赋值给root指针
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
//树不为空,按二叉搜索树性质查找插入位置,插入新节点
Node* parent = nullptr;//记录插入节点的前一个指针,方便插入时操作
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;//插入已有的值返回false
}
}
//找到位置,插入新节点
cur = new Node(key);
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;//插入完成
}
🌠二叉搜索树的删除
• 按照二叉搜索树的性质查找到要删除的的节点:
bool Erase(const K& key)
{
Node* parent = nullptr;
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;
}
此时该如何进行删除呢?
被删除的节点有以下几种情况:
• <1>被删除的节点无孩子节点
• <2>被删除的节点只有左孩子节点
• <3>被删除的节点只有右孩子节点
情况1、2、3可归为同一类情况,因为有无孩节点也是指向空;
//0-1个孩子的情况
if (cur->_left == nullptr)//被删除节点的左为空
{
//如果要删除的是没有左子树的 根节点,此时根节点是没有parent的
if (parent == nullptr)
{
_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)//被删除节点的右为空
{
//如果要删除的是没有右子树的 根节点,此时根节点是没有parent的
if (parent == nullptr)
{
_root = cur->_left;//直接让根节点指向右子树
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;//删除节点
return true;
}
else
{
//2个孩子的情况
//如何删除?
}
• <4>被删除的节点只有左、右孩子节点
情况4为另一类情况,该解决办法有两种,
方法一:选择被删除节点的右子树的最小值作为替代节点;
方法二:选择被删除节点的左子树的最大值作为替代节点。
这里选择方法一:
else
{
//2个孩子的情况
//法一:找要删除节点的右子树的最小节点作为替代节点
//法二:找要删除节点的左子树的最大节点作为替代节点
//找右子树的最小节点
//Node* rightMinP = nullptr;//rightMinP为rightMin的父亲,方便删除rightMin节点
Node* rightMinP = cur;//如果rightMinP为空,就不进入循环里面,就为空指针,所以直接指向cur
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;//一直往左找
}
cur->_key = rightMin->_key;//替代要删除的节点
//rightMin不一定是rightMinP的左,也有可能是右(rightMinP = cur)
if(rightMinP->_left == rightMin)
rightMinP->_left = rightMin->_right;
else
rightMinP->_right = rightMin->_right;
delete rightMin;
return true;
}
🌠二叉搜索树的中序遍历
public:
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
二、二叉搜索树(KV模型)的模拟实现
KV模型:每一个关键码key,都有与之对应的值value,即<Key,Value>的键值对。
KV的模型与K模型一样,只不过是多存了一个值 :
• 在查找时,找到返回节点的值,找不到返回nullptr;
• 在插入时,树为空,新建的节点要存value;
namespace Key
{
template<class K>
class BSTree
{
};
}
namespace KeyValue
{
template<class K, class V>
class BSTree
{
};
}
三、二叉搜索树的应用场景
K模型:门禁系统 / 判断英语单词是否拼写正确
KV模型:英汉词典 / 统计单词出现的次数
四、二叉搜索树的性能分析
• 插入和删除操作都必须先查找,查找效率代表了二叉搜索树中的各个操作性能。
• 对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
• 最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:O(log₂N)
• 最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:O(N)
五、模拟实现的代码
#pragma once
#include<iostream>
using namespace std;
namespace Key
{
template<class K>
struct BSTNode
{
K _key;
BSTNode<K>* _left;
BSTNode<K>* _right;
BSTNode(const K& key)
:_key(key)
, _left(nullptr)
, _right(nullptr)
{}
};
template<class K>
class BSTree
{
typedef BSTNode<K> Node;
public:
BSTree()
:_root(nullptr)
{}
bool Insert(const K& key)
{
//树为空,直接新增节点,赋值给root指针
if (_root == nullptr)
{
_root = new Node(key);
}
//树不为空,按二叉搜索树的性质查找插入位置,插入新节点
Node* parent = nullptr;
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
{
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 = nullptr;
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
{
//找到节点删除
//0-1个孩子
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_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 == nullptr)
{
_root = cur->_left;
}
else
{
if (parent->_left = cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else
{
//有2个孩子的删除
//找出该节点 右子树的最小节点
Node* rightMinP = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMinP->_left == rightMin)
rightMinP->_left = rightMin->_right;
else
rightMinP->_right = rightMin->_right;
delete rightMin;
return true;
}
}
}
//找不到节点
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
}
namespace KeyValue
{
template<class K, class V>//+
struct BSTNode
{
K _key;
V _value;//+
BSTNode<K, V>* _left;
BSTNode<K, V>* _right;
BSTNode(const K& key, const V& value)//+
:_key(key)
,_value(value)//+
, _left(nullptr)
, _right(nullptr)
{}
};
template<class K, class V>//+
class BSTree
{
typedef BSTNode<K, V> Node;//+
public:
BSTree()
:_root(nullptr)
{}
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 Insert(const K& key, const V& value)//+
{
if (_root == nullptr)
{
_root = new Node(key, value);//+
return true;
}
Node* parent = nullptr;
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
{
parent->_left = cur;
}
return true;
}
bool Erase(const K& key)
{
//元素是否在二叉搜索树上
Node* parent = nullptr;
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
{
//找到节点,进行删除
// 0-1 个孩子
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_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 == nullptr)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
return true;
}
else
{
// 删除有两个孩子的节点
//找被删除节点的 右子树的最小值
Node* rightMinP = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;
if (rightMinP->_left == rightMin)
rightMinP->_left = rightMin->_right;
else
rightMinP->_right = rightMin->_right;
delete rightMin;
return true;
}
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << ":" << root->_value << endl;;
_InOrder(root->_right);
}
private:
Node* _root = nullptr;
};
}
#include"SearchBinaryTree.h"
#include<string>
//int main()
//{
// int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
// //int a[] = { 8, 3, 3, 1, 3, 10, 6, 3, 4, 7, 5, 14, 13 };
//
// BSTree<int> t;
// for (auto e : a)
// {
// t.Insert(e);
// }
//
// //t.InOrder();
//
// t.Find(100);
//
// //t.Erase(4);
// //t.InOrder();
//
// //t.Erase(3);
// //t.InOrder();
//
// for (auto e : a)
// {
// t.Erase(e);
// t.InOrder();
// }
//
// return 0;
//}
int main()
{
KeyValue::BSTree<string, string> dic;
dic.Insert("left", "左边");
dic.Insert("right", "右边");
dic.Insert("string", "字符串");
dic.Insert("insert", "插入");
string str;
while (cin >> str)//string的流提取 ctrl+^Z +enter杀掉进程
{
//auto ret = dic.Find(str);
KeyValue::BSTNode<string, string>* ret = dic.Find(str);
if (ret)
{
cout << "->" << ret->_value << endl;
}
else
{
cout << "无此单词,请重新输入!!!" << endl;
}
}
return 0;
}
int main()
{
// 统计水果出现的次数
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉" };
KeyValue::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();
return 0;
}
如若对你有帮助,记得点赞、收藏、关注哦!
若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢^ ^ ~