经过我们上一篇博客的书写,我们可以了解到 map / multimap / set / multiset底层使用搜索二叉树实现的,那么搜索二叉树又是什么呢?下面我们就走进搜索二叉树的世界了解一下。
《一》搜索二叉树的概念
二叉搜索树又称二叉排序树,它可能是一个空树,也有可能具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- .若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- .它的左右子树也分别为二叉搜索树
下面,我就用图形试一试
= {5,3,4,1,7,8,2,6,0,9};
这就是基础的搜索二叉树的树状图
《二》增,删,查,找
在用代码实现之前,我们再了解一下,它的查找,
下面我们再看一下插入的过程分析:
最后,我们在看一下删除的过程分析:
看了这几个分析之后,我们接下来就要实现完成搜索二叉树的实现了。
#pragma once
#include<iostream>
using namespace std;
template<class K>
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template<class K>
class BSTree //Binary search tree
{
typedef BSTreeNode<K> Node;
public:
BSTree()
:_root(nullptr)
{}
~BSTree()
{}
bool Insert(const K& key)
{
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;
}
}
cur = new Node(key);
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->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
return cur;
}
}
}
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
//1.节点为空,父亲指向节点的右,删除节点
//2.节点右为空,父亲指向节点的左,删除节点
//3.节点的左右都不为空,找右数最左节点,或者左数最右节点
Node* del = cur;
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = _root->_left;
}
else
{
if (cur == parent->_left)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
}
else
{
//Node* p_replace = nullptr;//这里一定不会为空,因为他不会是根;
Node* p_replace = cur;
Node* replace = cur->_right;
while (replace->_left)
{
p_replace = replace;
replace = replace->_left;
}
cur->_key = replace->_key;
//删除replace节点
if (p_replace->_left == replace)
{
p_replace->_left = replace->_right;
}
else
{
p_replace->_right = replace->_right;
}
del = replace;
}
delete del;
return true;
}
}
return false;
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_key <<" ";
_InOrder(root->_right);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
Node* _root;
};
int TestBSTree()
{
/*BSTree<int> t;
t.Insert(3);
t.Insert(2);
t.Insert(4);
t.Insert(6);
t.Insert(5);
t.Insert(1);
t.InOrder();
t.Erase(5);*/
BSTree<int> t;
int a[] = { 2, 4, 3, 1, 6, 7, 5, 8, 9, 0 };
for (auto e : a)
{
t.Insert(e);
}
t.InOrder();
t.Erase(2);
t.InOrder();
t.Erase(8);
t.Erase(1);
t.Erase(5);
t.InOrder();
for (auto e : a)
{
t.Erase(e);
}
t.InOrder();
return 0;
}
我们再看一下实现的调用接口,就非常简单了:
int main()
{
TestBSTree();
return 0;
}
下面,我们就是执行以下程序,来看一下他的结果。
下面,我们还要看一下,它的性能分析。
《四》搜索二叉树的性能分析
- 删除和增加都是在查找的基础上实现的,所以查找的效率,代表了搜索二叉树的各个性能。
- 对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉树搜索平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
- 下面我们看一张图片来分析。
上面的:《 有单支 》—》》其实为《《—《 右单只 》
最优的情况:搜索二叉树为完全二叉树,其平均比较次数为:log2 N;
最坏的情况:搜索二叉树为单支结构,其平均比较次数为:frac{N}{2};
问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,都可以是二叉搜索树的性能最佳?
下面我们就要实现以下那些优化的树了,我会把实现的链接放到下面的,
这就是我们实现的搜索二叉树的思想和实现代码,可以借鉴一下,有什么问题,可以在下面的评论区,询问我。