二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树,满足以下性质:
如下图:通过该图分析一下,并总结得出结论。
-1=N,注这里的h表示树的高度,N表示树中的节点个数。 即
,根据时间复杂度的规则,常数给省略,即
如下图:
高度为: h = N-1,。
)级别效率,但存在两大缺陷:
随着大数据时代的到来,自然语言处理、图像识别等领域需要快速高效的数据存储和检索机制。尽管二叉搜索树在某些情况下存在平衡性问题,未来可能会结合自平衡的树结构(如AVL树、红黑树)来改进性能。此外,随着并行计算技术的发展,研究如何在分布式系统中应用二叉搜索树的思想,将是一个重要的研究方向。 在智能算法和机器学习领域,二叉搜索树的基本原则也可能被拓展和应用于更复杂的模型和数据结构中。总的来说,二叉搜索树作为一种基础的数据结构,具有广泛的应用前景和深远的研究意义。
插入过程思考步骤:
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
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->_left = cur;
if (parent->_key < key)
parent->_right = cur;
return true;//表示插入成功
}
这个过程就简单了,当前要查的节点的值与根节点的值进行比较,比它大的往右走,反之往左走;走到空,就没找到,返回false即可,否则返回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* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
if (cur->_left == nullptr)
{
//左为空,父亲指向我的右
if (cur == parent->_right)
{
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
delete cur;
}
else if (cur->_right == nullptr)
{
//右为空,父亲指向我的左
if (cur == parent->_right)
{
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
delete cur;
}
else
{
//...
}
return true;
}
}
return false;
}
上述代码有没有问题??? 我们来看加入我们删除8这个节点,会有什么问题,parent为空,在else进行解引用,程序必崩溃。 如何解决??? 如果当前根节点左为空,且正要删除根节点,重新将根节点的右节点当做根节点即可,在释放旧的根节点;当前根节点右为空,且正要删除根节点,重新将根节点的左节点当做根节点即可,在释放旧的根节点,否则再让父节点指向要删除节点的左或右节点。
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
//左为空,父亲指向我的右
if (cur == parent->_right)
{
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
//右为空,父亲指向我的左
if (cur == parent->_right)
{
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
}
delete cur;
}
else
{
//...
}
return true;
}
}
return false;
}
当删除的节点既有左孩子又有右孩子,使用替代法。
找个符合二叉搜索树规则的节点来替代,
为什么行??? 因为左子树节点最大的值,比左子树所有节点的值都大,比右子树所有节点的值都小,可能站稳脚跟。本篇以第二种为例: 假如删除3,从右子树找最小的值4,从右子树的当前根节点,一直去左子树找,因为最小值在左子树,找到4,再将最小节点的值与当前正要删除节点的值(3)进行替换,从而转化成间接删除3这个节点。注意旧节点的右子树不一定为空。
else
{
//找右子树最小的最小节点,进行交换,间接删除要删除的节点。
Node* minright = cur->_right;
Node* minrightparent = cur;
while (minright->_left)
{
minrightparent = minright;
minright = minright->_left;
}
cur->_key = minright->_key;//交换值,达到删除的目的
if (minright == minrightparent->_right)
{
minrightparent->_right = minright->_right;
}
else
{
minrightparent->_left = minright->_right;
}
delete minright;
}
下面的代码是上面分析的整合版。
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
{
//删除
if (cur->_left == nullptr)
{
//if (parent == nullptr)
if (cur == _root)
{
_root = cur->_right;
}
else
{
// 父亲指向我的右
if (cur == parent->_right)
{
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
// 父亲指向我的左
if (cur == parent->_right)
{
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
}
delete cur;
}
else
{
// 找右子树最小节点(最左)替代我的位置
Node* minRightParent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
minRightParent = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (minRightParent->_left == minRight)
{
minRightParent->_left = minRight->_right;
}
else
{
minRightParent->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}
当数据仅需通过键进行快速查找、排序或范围查询,且无需存储额外数据时,纯键结构更合适。典型场景包括:
当需要通过键快速访问关联的额外数据时,键值对结构更适用。典型场景包括:
仅需在节点中增加value值,Find找到时返回节点的指针,以便对当前key对应的value值进行修改。
namespace key_value
{
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:
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;
}
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 = 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
{
//删除
if (cur->_left == nullptr)
{
//if (parent == nullptr)
if (cur == _root)
{
_root = cur->_right;
}
else
{
// 父亲指向我的右
if (cur == parent->_right)
{
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
}
delete cur;
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
// 父亲指向我的左
if (cur == parent->_right)
{
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
}
delete cur;
}
else
{
// 找右子树最小节点(最左)替代我的位置
Node* minRightParent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
minRightParent = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (minRightParent->_left == minRight)
{
minRightParent->_left = minRight->_right;
}
else
{
minRightParent->_right = minRight->_right;
}
delete minRight;
}
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);
}
Node* _root = nullptr;
};
}
Test.cpp
#include"BStree.h"
int main()
{
string arr[] = { "苹果","香蕉","香蕉","西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉","香蕉","香蕉" };
key_value::BSTree<string, int> countTree;
for (auto& e : arr)
{
//key_value::BSTNode<string, int>* ret = countTree.Find(e);
auto ret = countTree.Find(e);
if (ret == nullptr)
{
countTree.Insert(e, 1);
}
else
{
ret->_value++;
}
}
countTree.InOrder();
return 0;
}
输出结果:
本文介绍二叉搜索树(BST)是动态有序数据结构,通过左小右大性质实现高效增删查改(平均O(log n)),但最差退化为O(n)。其核心价值体现在:1)数据库索引、编译器符号表等需快速范围查询场景;2)自平衡变体(如红黑树)解决退化问题,保障高频操作性能;3)键值对扩展支持缓存系统、配置管理等关联数据场景。本文详述了BST的插入、查找、删除实现(含单/双子节点处理),并通过词频统计案例演示键值对应用。随着技术发展,BST与自平衡、分布式计算的融合将持续赋能实时系统与大数据处理。