1、概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值;
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值;
它的左右子树也分别为二叉搜索树。
2、操作
2.1增加(插入)
- 树为空,直接插入,返回true;
- 树不为空,判断根节点数据大小,根数据大往左边找,否则往右边找,找到返回true;
- 节点不存在返回false;
2.2删除
- 先查找到相应节点,没找到返回false;
- 找到后根据情况进行判断。如下图:
①当删除的节点左、右节点都为空时,即叶节点,如节点值为8、19、33、68、105的节点,直接删除即可,并将其父节点对应的节点指针置为空;
②当删除的节点左节点或右节点为空时,即度为1的节点,如节点值为25、86的节点,在删除该节点时要将其父节点指向删除节点的指针指向删除节点的不为空的子节点,图表示如下:
③当删除的节点有两个子节点,即度为2时,如节点值为13、32、45、99的节点,因为直接删除后该改变数的大结构,相当于重新构建树了,所以可以找适合当前节点值得节点,使其数据进行替换,再删除另一个节点就更方便了。这时应该选择②情况中的节点,便于操作。实际上,只要找到要删除节点左子树最右边的节点或者右子树左边的节点进行删除即可。
如删除值为45的节点,找到右子树最左边的节点minright,即值为68的节点,使值为45的节点值替换为68,再删除节点minright即可。
但在删除根节点时要把树的根改变,所以这时要对条件进行判断,具体细节见后面的代码;
2.3查找
利用二叉搜索树的性质进行查找,查找的值比节点值小时往节点左子树找,比节点值大时往节点右边找。
2.4修改
在查找的基础上进行修改。
3、 二叉搜索树的性能分析
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log2(N);
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2。
所以二叉搜索树的时间复杂度为N。
4、模拟实现代码
#pragma once
#include<iostream>
#include<string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
namespace XL
{
template<class K, class V>
struct BSTreeNode
{
BSTreeNode(const K&key, const V& value)
:_left(nullptr)
,_right(nullptr)
,_key(key)
,_value(value)
{}
BSTreeNode<K,V>* _left;
BSTreeNode<K,V>* _right;
K _key;
V _value;
};
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
public:
bool Insert(const K& key, const V& value){
if (_root == nullptr) {
_root = new Node(key, value);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur) {
if (cur->_key > key) {
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key) {
parent = cur;
cur = cur->_right;
}
else
return false;
}
cur = new Node(key, value);
if (parent->_key > key) {
parent->_left = cur;
}
else if (parent->_key < key) {
parent->_right = 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;
}
return nullptr;
}
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 {
//要删除节点左边为空
if (cur->_left == nullptr) {
//删除根节点时改变根节点
if (cur == _root)
_root = cur->_right;
else {
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
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 {
//找该节点左子树最右边(大)的节点(maxleft),或者右子树最左边(小)的节点(minright),
//使要删除节点的数据等于上面节点,然后删除找的节点,这里找minright
Node* minright = cur->_right;
Node* minparent = cur;
while (minright->_left) {
//保存"删除"节点的父亲
minparent = minright;
minright = minright->_left;
}
if (minparent->_left == minright)
minparent->_left = minright->_right;
else
minparent->_right = minright->_right;
cur->_key = minright->_key;
cur->_value = minright->_value;
delete minright;
}
return true;
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
}
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;
};
void TestBSTree1()
{
BSTree<int,string>bst1;//双参数模板
bst1.Insert(32,"a");
bst1.Insert(45,"b");
bst1.Insert(99,"c");
bst1.Insert(25,"d");
bst1.Insert(13,"e");
bst1.Insert(27,"f");
bst1.Insert(86,"g");
bst1.Insert(68,"h");
bst1.InOrder();
cout<<bst1.Find(13)<<endl;
cout << bst1.Find(68) << endl;
cout << bst1.Find(0) << endl;
bst1.Erase(13);
bst1.InOrder();
cout << endl;
bst1.Erase(32);
bst1.InOrder();
cout << endl;
int arr[] = { 32,45,99,25,13,27,86,68 };
//测试删除结果
for (auto& e : arr)
{
bst1.Erase(e);
}
bst1.InOrder();
}
};