文章目录
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树

二叉搜索树的整体框架:
//节点的定义
template<class K>
struct BSTreeNode {
BSTreeNode<K>* _left;//左孩子
BSTreeNode<K>* _right;//右孩子
K _key;//节点数据
//构造函数
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
private:
//根节点
Node* _root = nullptr;
};
- 从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
- 最多查找高度次,走到到空,还没找到,这个值不存在。
💕 代码实现:
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_key) {
cur = cur->_right;
}
else if (key < cur->_key) {
cur = cur->_left;
}
else {
return true;
}
}
return false;
}
插入的具体过程如下:
当根节点不为空时,又分为两种插入方式:
(1) 如果要插入的值比根大就往右边走,比根小就往左边走。直到找到为空的位置,然后插入。
(2) 如果查找过程中遇到与要插入节点相同的节点,直接返回false,因为K模型中不允许出现key值相同的节点。

💕 代码实现:
bool Insert(const K& key)
{
//根节点为空的情况
if (_root == nullptr) {
_root = new Node(key);
return true;
}
//根节点不为空的情况
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key > cur->_key) {
parent = cur;
cur = cur->_right;
}
else if (key < cur->_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;
}
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面三种情况:
- 情况1:删除的节点为叶子节点,此时我们只需要让父节点的 left/right 指向空,然删除掉叶子节点即可——直接删除。
- 情况2:删除的节点只有左孩子或者只有右孩子,此时我们需要将该节点的子节点托孤给父节点,然后再删除掉该节点——直接删除。
- 情况3:要删除的节点既有左孩子又有右孩子,此时我们需要先将该节点与左子树的最大值/最右节点或者右子树的最小/最左节点进行交换,然后再delete掉被替换的最左节点/最右节点——替换删除。
当然了,这里的情况1可以归纳到情况2中,因为左右都为空也属于左为空或者右为空的情况。
💕 左为空或者右为空的情况:

💕 左右孩子都不为空的情况:

这里我们还需要注意一个细节:
如果右孩子或者左孩子为空,且要删除的节点是根节点时,需要特殊判断一下,直接让根节点指向左孩子或者右孩子即可。否则的话就会对根节点进行解引用,程序发生奔溃。
💕 代码实现:
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key > cur->_key) {
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key) {
parent = cur;
cur = cur->_left;
}
else {
//1. 删除操作——左孩子为空
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (parent->_right == cur) {
parent->_right = cur->_right;
}
else {
parent->_left = cur->_right;
}
}
delete cur;
}
//2. 删除操作——右孩子为空
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_right == cur) {
parent->_right = cur->_left;
}
else {
parent->_left = cur->_left;
}
}
delete cur;
}
//两个孩子都不为空
else
{
//3.其他情况该如何办呢?——找右树最小节点或者左树最大节点来代替
Node* minRight = cur->_right;
Node* pminRight = cur;
while (minRight->_left)
{
pminRight = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (pminRight->_right == minRight) {
pminRight->_right = minRight->_right;
}
else {
pminRight->_left = minRight->_right;
}
delete minRight;//这里我们要注意删除的节点是minRight而不是cur否则程序会奔溃
}
return true;
}
}
return false;
}
💕 查找递归实现
//递归查询
bool FindR(const K& key)
{
return _FindR(_root, key);
}
//查找递归方法
bool _FindR(Node* root, const K& key)
{
if (root == nullptr)
return false;
else if (key > root->_key)
return _FindR(root->_right, key);
else if (key < root->_key)
return _FindR(root->_left, key);
else
return true;
}
💕 插入递归实现
//递归插入
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
//插入递归方法
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr) {
root = new Node(key);
return true;
}
if (key > root->_key) {
return _InsertR(root->_right, key);
}
else if (key < root->_key) {
return _InsertR(root->_left, key);
}
else {
return false;
}
}
💕 删除递归实现
//递归删除
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
//删除递归方法
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
return false;
if (key > root->_key) {
return _EraseR(root->_right, key);
}
else if (key < root->_key) {
return _EraseR(root->_left, key);
}
else
{
//正式开始删除操作
Node* del = root; //先保存根节点以便于后续删除
//1.左孩子为空
if (root->_left == nullptr) {
root = root->_right;
}
//2.右孩子为空
else if (root->_right == nullptr) {
root = root->_left;
}
//3.两个孩子都不为空——难点哦!
else
{
//找左子树的最大值来替代待删除的节点
Node* leftMax = root->_left;
while (leftMax->_right)
{
leftMax = leftMax->_right;
}
//交换两个节点的key值
swap(root->_key, leftMax->_key);
return _EraseR(root->_left, key);
}
delete del;
return true;
}
}
当左孩子为空或者右孩子为空时,因此此时的root是其父节点左指针或者右指针的别名,因此我们只需要改变root的指向,root = root->_left 或者 root = root->_right,就可以改变父节点左指针或者右指针的指向。
当左孩子和右孩子都不为空时,我们可以找左子树的最右节点或者右子树的最左节点,这里我们以左子树的最左节点为例。将该节点的Key值和要删除的节点的Key值进行交换,然后再调用_EraseR(root->_left, key);删除替换后的节点即可。

//节点的定义
template<class K>
struct BSTreeNode {
BSTreeNode<K>* _left;//左孩子
BSTreeNode<K>* _right;//右孩子
K _key;//节点数据
//构造函数
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
//二叉搜索树的类模板定义
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
BSTree() = default;//强制生成默认构造函数
//拷贝构造函数
BSTree(const BSTree<K>& b)
{
_root = Copy(b._root);
}
//赋值运算符重载函数
BSTree<K>& operator=(BSTree<K> k)
{
swap(_root, k._root);
return *this;
}
//析构函数
~BSTree()
{
Destroy(_root);
}
//二叉树的插入
bool Insert(const K& key)
{
//根节点为空的情况
if (_root == nullptr) {
_root = new Node(key);
return true;
}
//根节点不为空的情况
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key > cur->_key) {
parent = cur;
cur = cur->_right;
}
else if (key < cur->_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 (key > cur->_key) {
cur = cur->_right;
}
else if (key < cur->_key) {
cur = cur->_left;
}
else {
return true;
}
}
return false;
}
//二叉树的删除操作
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key > cur->_key) {
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key) {
parent = cur;
cur = cur->_left;
}
else {
//1. 删除操作——左孩子为空
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (parent->_right == cur) {
parent->_right = cur->_right;
}
else {
parent->_left = cur->_right;
}
}
delete cur;
}
//2. 删除操作——右孩子为空
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_right == cur) {
parent->_right = cur->_left;
}
else {
parent->_left = cur->_left;
}
}
delete cur;
}
//两个孩子都不为空
else
{
//3.其他情况该如何办呢?——找右树最小节点或者左树最大节点来代替
Node* minRight = cur->_right;
Node* pminRight = cur;
while (minRight->_left)
{
pminRight = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (pminRight->_right == minRight) {
pminRight->_right = minRight->_right;
}
else {
pminRight->_left = minRight->_right;
}
delete minRight;//这里我们要注意删除的节点是minRight而不是cur否则程序会奔溃
}
return true;
}
}
return false;
}
//搜索二叉树的中序遍历——这儿需要注意_root和root的名字不要搞混,不然程序会奔溃
void InOrder()
{
_InOrder(_root);
cout << endl;
}
//递归方式实现以上的接口
//递归插入节点
bool InsertR(const K& key)
{
return _InsertR(_root, key);
}
//递归查询
bool FindR(const K& key)
{
return _FindR(_root, key);
}
//递归删除
bool EraseR(const K& key)
{
return _EraseR(_root, key);
}
private:
//拷贝函数
Node* Copy(Node* root)
{
//先将每一个节点拷贝出来,最后递归结束的时候在依次链接起来
if (root == nullptr)
return nullptr;
Node* cur = new Node(root->_key);
cur->_left = Copy(root->_left);
cur->_right = Copy(root->_right);
return cur;
}
//释放内存函数——后续遍历删除
void Destroy(Node*& root)
{
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
//中序遍历
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
//插入递归方法
bool _InsertR(Node*& root, const K& key)
{
if (root == nullptr) {
root = new Node(key);
return true;
}
if (key > root->_key) {
return _InsertR(root->_right, key);
}
else if (key < root->_key) {
return _InsertR(root->_left, key);
}
else {
return false;
}
}
//查找递归方法
bool _FindR(Node* root, const K& key)
{
if (root == nullptr)
return false;
else if (key > root->_key)
return _FindR(root->_right, key);
else if (key < root->_key)
return _FindR(root->_left, key);
else
return true;
}
//删除递归方法
bool _EraseR(Node*& root, const K& key)
{
if (root == nullptr)
return false;
if (key > root->_key) {
return _EraseR(root->_right, key);
}
else if (key < root->_key) {
return _EraseR(root->_left, key);
}
else
{
//正式开始删除操作
Node* del = root; //先保存根节点以便于后续删除
//1.左孩子为空
if (root->_left == nullptr) {
root = root->_right;
}
//2.右孩子为空
else if (root->_right == nullptr) {
root = root->_left;
}
//3.两个孩子都不为空——难点哦!
else
{
//找左子树的最大值来替代待删除的节点
Node* leftMax = root->_left;
while (leftMax->_right)
{
leftMax = leftMax->_right;
}
//交换两个节点的key值
swap(root->_key, leftMax->_key);
return _EraseR(root->_left, key);
}
delete del;
return true;
}
}
private:
//根节点
Node* _root = nullptr;
};
例如:
💕 统计每种水果出现的次数:
namespace KeyVal
{
//节点的定义
template<class K,class V>
struct BSTreeNode {
BSTreeNode<K,V>* _left;//左孩子
BSTreeNode<K,V>* _right;//右孩子
K _key;//节点数据
V _val;
//构造函数
BSTreeNode(const K& key,const V& val)
: _left(nullptr)
, _right(nullptr)
, _key(key)
, _val(val)
{}
};
//二叉搜索树的类模板定义
template<class K,class V>
class BSTree
{
typedef BSTreeNode<K,V> Node;
public:
//二叉树的插入
bool Insert(const K& key,const V& val)
{
//根节点为空的情况
if (_root == nullptr) {
_root = new Node(key,val);
return true;
}
//根节点不为空的情况
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key > cur->_key) {
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key) {
parent = cur;
cur = cur->_left;
}
else {
return false;
}
}
//链接节点
cur = new Node(key,val);
if (parent->_key < key) {
parent->_right = cur;
}
else {
parent->_left = cur;
}
return true;
}
//二叉树的查询
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_key) {
cur = cur->_right;
}
else if (key < cur->_key) {
cur = cur->_left;
}
else {
return cur;
}
}
return nullptr;
}
//二叉树的删除操作
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key > cur->_key) {
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key) {
parent = cur;
cur = cur->_left;
}
else {
//1. 删除操作——左孩子为空
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (parent->_right == cur) {
parent->_right = cur->_right;
}
else {
parent->_left = cur->_right;
}
}
delete cur;
}
//2. 删除操作——右孩子为空
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_right == cur) {
parent->_right = cur->_left;
}
else {
parent->_left = cur->_left;
}
}
delete cur;
}
//两个孩子都不为空
else
{
//3.其他情况该如何办呢?——找右树最小节点或者左树最大节点来代替
Node* minRight = cur->_right;
Node* pminRight = cur;
while (minRight->_left)
{
pminRight = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (pminRight->_right == minRight) {
pminRight->_right = minRight->_right;
}
else {
pminRight->_left = minRight->_right;
}
delete minRight;//这里我们要注意删除的节点是minRight而不是cur否则程序会奔溃
}
return true;
}
}
return false;
}
//搜索二叉树的中序遍历——这儿需要注意_root和root的名字不要搞混,不然程序会奔溃
void InOrder()
{
_InOrder(_root);
cout << endl;
}
protected:
//中序遍历
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key << ":" << root->_val << endl;
_InOrder(root->_right);
}
private:
//根节点
Node* _root = nullptr;
};
}
//test.cpp
void TestBSTree4()
{
string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };
KeyVal::BSTree<string, int> bs;
for (auto e : arr)
{
auto ret = bs.Find(e);
if (ret == nullptr)
bs.Insert(e, 1);
else
ret->_val++;
}
bs.InOrder();
}

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二
叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树;
- 最优情况下,二叉搜索树为完全二叉树 (或者接近完全二叉树),其平均比较次数为 O(logN)。
- 最差情况下,二叉搜索树退化为单支树( 或者类似单支),其平均比较次数为 O(N)。
只有当数据有序或接近有序时二叉搜索树查找数据的时间复杂度才为 O(N),大部分情况下查找效率都是要远高于 O(N) 的;同时,在实际开发中我们用的并不是单纯的二叉搜索树树,而是它的改进版 – 二叉搜索平衡树 (AVL树) ,即插入数据后对二叉搜索树进行调整,让其左右子树尽量变得平衡,这样,二叉搜索树的查找效率就几乎等于 O(logN) 了。
我使用Nokogiri(Rubygem)css搜索寻找某些在我的html里面。看起来Nokogiri的css搜索不喜欢正则表达式。我想切换到Nokogiri的xpath搜索,因为这似乎支持搜索字符串中的正则表达式。如何在xpath搜索中实现下面提到的(伪)css搜索?require'rubygems'require'nokogiri'value=Nokogiri::HTML.parse(ABBlaCD3"HTML_END#my_blockisgivenmy_bl="1"#my_eqcorrespondstothisregexmy_eq="\/[0-9]+\/"#FIXMEThefoll
寻找有用的ruby的好网站是什么? 最佳答案 AgileWebDevelopment列出插件(虽然不是rubygems,我不确定为什么),并允许人们对它们进行评级。RubyToolbox按类别列出gem并比较它们的受欢迎程度。Rubygems有一个搜索框。StackOverflow对最有用的rails插件和rubygems有疑问。 关于ruby-如何搜索有用的ruby,我们在StackOverflow上找到一个类似的问题: https://stacko
我有很多这样的文档:foo_1foo_2foo_3bar_1foo_4...我想通过获取foo_[X]的所有实例并将它们中的每一个替换为foo_[X+1]来转换它们。在这个例子中:foo_2foo_3foo_4bar_1foo_5...我可以用gsub和一个block来做到这一点吗?如果不是,最干净的方法是什么?我真的在寻找一个优雅的解决方案,因为我总是可以暴力破解它,但我觉得有一些正则表达式技巧值得学习。 最佳答案 我(完全)不懂Ruby,但类似这样的东西应该可以工作:"foo_1foo_2".gsub(/(foo_)(\d+)/
我读了"BingSearchAPI-QuickStart"但我不知道如何在Ruby中发出这个http请求(Weary)如何在Ruby中翻译“Stream_context_create()”?这是什么意思?"BingSearchAPI-QuickStart"我想使用RubySDK,但我发现那些已被弃用前(Rbing)https://github.com/mikedemers/rbing您知道Bing搜索API的最新包装器(仅限Web的结果)吗? 最佳答案 好吧,经过一个小时的挫折,我想出了一个办法来做到这一点。这段代码很糟糕,因为它是
给定一个元素和一个数组,Ruby#index方法返回元素在数组中的位置。我使用二进制搜索实现了我自己的索引方法,期望我的方法会优于内置方法。令我惊讶的是,内置的在实验中的运行速度大约是我的三倍。有Rubyist知道原因吗? 最佳答案 内置#indexisnotabinarysearch,这只是一个简单的迭代搜索。但是,它是用C而不是Ruby实现的,因此自然可以快几个数量级。 关于Ruby#index方法VS二进制搜索,我们在StackOverflow上找到一个类似的问题:
我一直在尝试在Ruby中实现BinaryTree类,但我得到了stackleveltoodeep错误,尽管我似乎没有在该特定代码段中使用任何递归:1.classBinaryTree2.includeEnumerable3.4.attr_accessor:value5.6.definitialize(value=nil)7.@value=value8.@left=BinaryTree.new#stackleveltoodeephere9.@right=BinaryTree.new#andhere10.end11.12.defempty?13.(self.value==nil)?true:
我有一个表,'jobs'和一个枚举字段'status'。status具有以下枚举集:enumstatus:[:draft,:active,:archived]使用ransack,我如何过滤表,比如说,所有事件记录? 最佳答案 你可以像这样在模型中声明自己的掠夺者:ransacker:status,formatter:proc{|v|statuses[v]}do|parent|parent.table[:status]end然后您可以使用默认的搜索语法_eq来检查相等性,如下所示:Model.ransack(status_eq:'ac
我一直在使用postgres关注railscast的全文搜索,但我不断收到以下错误#的未定义局部变量或方法“作用域”我关注了railscast确切地。我安装了所有正确的gem。(pg_search,pg)。这是我的代码文章Controller(我在这里也使用acts_as_taggable)defindex@articles=Article.text_search(params[:query]).page(params[:page]).per_page(3)ifparams[:tag]@articles=Article.tagged_with(params[:tag])else@art
我想使用部分字符串搜索数组,然后获取找到该字符串的索引。例如:a=["Thisisline1","Wehaveline2here","andfinallyline3","potato"]a.index("potato")#thisreturns3a.index("Wehave")#thisreturnsnil使用a.grep将返回完整的字符串,使用a.any?将返回正确的true/false语句,但都不会返回匹配的索引找到了,或者至少我不知道该怎么做。我正在编写一段代码,该代码读取文件、查找特定header,然后返回该header的索引,以便它可以将其用作future搜索的偏移量。如果
我不确定如何为我的搜索功能添加自动完成表单。"get"do%>nil%>我有一个具有自定义操作的Controllerdefquery@users=Search.user(params[:query])@article=Search.article(params[:query])end模型如下:defself.user(search)ifsearchUser.find(:all,:conditions=>['first_nameLIKE?',"%#{search}%"])elseUser.find(:all)endenddefself.article(search)ifsearchArt