前言
最近把笔记软件改成了obsidian了,也是一个朋友给我推荐的。语雀其实挺好用的,但是感觉obsidian的上限更高,并且在看了卡片盒笔记法后,我觉得知识是要成网络的。而语雀没有这样的能力,obsidian的网络节点功能能更好地帮我梳理知识点之间的关系。好的工具是大脑的延申嘛 现在的问题是,我语雀的快捷键用的很习惯了,而obsidian的快捷键还在熟悉阶段,一些功能也没有,书写也全是markdown格式,需要慢慢熟悉了。但我相信等我熟悉了之后,效率会得到提高。以前语雀记录的内容,往往很少去复习,后面使用obsidian了,我希望我能有效地复习,把知识点都串联起来 人总是要不断地踏出舒适圈,才能得到最快地提升,包括后面我可能去练习一下vim的操作,不过那是后话了~
今天我们来看一下二叉搜索树,作为学习map/set的前置知识
二叉搜索树
什么是二叉搜索树?其实刷过Leetcode我们应该都不陌生,它本质上是一种二分的思想,通过二分法,将线性查找的时间复杂度优化到了对数时间复杂度。二叉搜索树也很简单,根据递归的思想,它只需要满足以下性质
- 左子树的所有节点都小于根节点的值
- 右子树的所有节点都大于根节点的值
- 左右子树同时满足上述条件
光靠看是没用的,这里放一些二叉搜索树的题目,做一做,相信大家对其的理解会加深不少
实现思路
我们要实现一个二叉搜索树,就要考虑它的增删改查,其中改比较简单,通过查找到它的位置后修改对应的值即可,所以这里我们讲一下增删查的思路。
查
从根节点开始搜索,比根节点小走左节点,比根节点大走右节点
到空节点都未找到,说明目标节点不存在
增
树为空,直接插入并返回
按照查的思路,找到插入位置,插入新节点
删
查找要删除的节点是否存在,不存在返回,存在则根据目标节点以下状态:
- 只有左孩子
- 只有右孩子
- 没有子节点
- 既有左孩子也右孩子
其中12两种情况是:直接将被删除节点的父节点指向被删除节点的子节点,4则需要在左子树中找到最大节点(或者右子树中找到最小节点)与被删除节点互换,并删除该节点。
代码实现
namespace mystd
{
template<class T>
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
T _value;
BSTreeNode(T value)
:_left(nullptr)
,_right(nullptr)
,_value(value)
{}
};
template<class T>
class BSTree
{
typedef BSTreeNode<T> Node;
public:
bool Insert(const T& x)
{
if (_root == nullptr)
{
_root._value = x;
return true;
}
Node cur = _root;
Node parent = nullptr;
while (cur)
{
if (x < cur->_value)
{
parent = cur;
cur = cur->_left;
}
else if (x > cur->_value)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(x);
if (x < parent->_value)
parent->_left = cur;
else
parent->_right = cur;
return true;
}
bool Find(const T& x)
{
Node* cur = _root;
while (cur)
{
if (x < cur->_value)
cur = cur->_left;
else if (x > cur->_value)
cur = cur->_right;
else
return true;
}
return false;
}
bool Erase(const T& x)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (x < cur->_value)
{
parent = cur;
cur = cur->_left;
}
else if (x < cur->_value)
{
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->_left;
}
delete cur;
}
// 右子树空
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;
}
else
{
Node* leftMaxParent = cur;
Node* leftMax = cur->_left;
while (leftMax->_right)
{
leftMaxParent = leftMax;
leftMax = leftMax->_right;
}
cur->_value = leftMax->_value;
if (leftMaxParent->_left == leftMax)
leftMaxParent->_left = leftMax->_left;
else
leftMaxParent->_right = leftMax->_left;
delete leftMax;
}
return true;
}
}
return false;
}
private:
Node _root = nullptr;
};
}
结束语
自己的代码能力确实依然有所欠缺,二叉搜索树依然花了大量的时间才写好。可能明天看能不能看完set/map的源码吧