概述
这篇文章将阐述我对于搜索二叉树的理解,以及底层的模拟实现,请大家理性阅读,这是我第一次写博客,错误可能有点多,请大家多多包涵,希望对你有帮助!
基础知识
搜索二叉树的介绍
AVLTree和红黑树都是建立在搜索二叉树的基础之上的,首先我们来理解搜索二叉树,二叉搜索树又称为二叉排序树,或者是一颗空树,具有以下的性质,如果左子树的不为空,则左子树上所有的节点的值都小于根节点的值,若它的右子树不为空,则右子树上所有的节点的值都大于根节点的值,它的左右子树也都是搜索二叉树
如果按照int a[]={5,3,4,1,7,8,2,6,0,9}的方式构建搜索二叉树,得到如下的搜索二叉树
不难看出如果按照中序遍历的方式来看的话,得到就是 0,1,2,3,4,5,6,7,8,9,是一个排序好的序列。
那么搜索二叉树的意义在哪?如图所示
如果树比较平衡的话,那么搜索次数最大就是树的高度次,也就是o(logN),效率很高,但是如果是极端的情况,就会退化,如图所示
这样的话,在右单支的情况效率就退化成o(N),所以为了解决可能出现的情况,于是就有了AVLTree和红黑树的诞生!
在分析AVLTree和红黑树之前,我们先来实现一个搜索二叉树的插入和删除操作,首先二叉树的插入是十分简单的,只要满足搜索二叉树的规则就可以实现。首先给搜索二叉树定一个基本的框架
template<class K>
class BSTreeNode
{
BSTreeNode*_left;
BSTreeNode*_right;
K _key;
BSTreeNode(const K& key)
{
:_left(nullptr)
,_right(nullptr)
,_left(nullptr)
};
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
private:
Node*_root=nullptr;
};
首先是实现一个BSTreeNode,这个作为节点,然后就是BSTree,这个就是作为的搜索二叉树的代码,增删查的功能都是在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;//相同就返回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 (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
删除节点
删除数据是相比较而言就要复杂很多的,主要就是有很多的情况是需要分析的,读者可以先自己尝试构建一个二叉树然后去删除几个节点,总结一下各种可能会遇到的情况
情况1 首先是删除节点左右子树都为空,这是最简单的情况,只需要直接删除即可 情况2 左子树为空,但是右子树不为空 情况3 左子树不为空,但是右子树为空 情况4 左右子树都不为空
单独分析一下情况4,当左右子树都不为空的时候,这个时候情况比较复杂,解决办法就是转移问题,其实我们只是需要将这个值删除,而这个值的所在的节点是左右子树都存在的情况,所以我们采用交换的方式将情况4转变为之前的情况。
假设我们删除12,为了改变整棵树的结构,最好是能够找到一个值来替代12的删除,但是要满足搜索二叉树的规则,其实就是以12为根的子树的左子树的最右节点和右子树的最左节点,这个
然后只需要删除红色的节点,就转换成情况简单的情况
具体的代码实现
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
{
// 1、左为空
// 2、右为空
// 3、左右都不为空,替换删除
if (cur->_left == nullptr)
{
//if (parent == 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 (parent == 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* parent = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
parent = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (minRight == parent->_left)
{
parent->_left = minRight->_right;
}
else
{
parent->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}