概念
什么是搜索二叉树?搜索二叉树有什么特点?
它的特点就是任意节点的左右子树,一个子树全都比该节点的值大,另一个子树全都比该节点的值要小
节点的结构
//节点
template<class K>
struct BSTNode
{
BSTNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{
}
BSTNode<K>* _left;
BSTNode<K>* _right;
K _key;
};
节点的插入
搜索二叉树中另一个特点就是没有重复的值,如果插入的节点在该树中已经存在,那么就不会再进行插入,如果不存在那么就在树的叶子结点的null节点处,找到合适的位置进行插入
//循环实现
bool insert(const K& key)
{
Node* newnode = new Node(key);
if (_root == nullptr)
{
_root = newnode;
return true;
}
Node* cur = _root;
Node* parent = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
if (parent->_key > key)
{
parent->_left = newnode;
}
else
{
parent->_right = newnode;
}
return true;
}
//递归实现
bool _Insert(const K& key, Node*& root)//指针的引用
{
if (root == nullptr)//root此时是别名
{
root = new Node(key);
return true;
}
if (root->_key > key)
{
return _Insert(key, root->_left);
}
else if (root->_key < key)
{
return _Insert(key, root->_right);
}
else
{
return false;
}
}
删除操作
如果找到那么久删除返回true,如果没找到则返回false
难点在于删除某一个节点后,其它的节点应该怎么改变,才能继续保证二叉搜索树的性质
对于要删除的节点有以下四种情况:
无孩子节点,只有左孩子,只有右孩子,左右孩子都有
解决方案:
无孩子节点:直接删除即可
只有左孩子节点:让左孩子节点代替该节点的位置即可
只有右孩子节点:让右孩子节点代替该节点位置即可
既有左孩子又有右孩子:两种解决方案(假定左子树比该节点小,右子树比该节点大),1.让左子树的最大节点代替该位置2.让右子树最小节点代替该位置
//循环实现
bool erase(const K& key)
{
Node* cur = _root;
Node* parent = _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.该节点左右都不为空 //4.该节点左右都为空(被归类到第一种情况中了)
if (cur->_left == nullptr)
{
if (cur == _root)//根节点就是要找的节点
{
_root = _root->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
}
else if (cur->_right == nullptr)
{
if (cur == _root)//根节点就是要找的节点
{
_root = _root->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
}
else//该节点左右都不为空
{
//找替代节点//从当前cur节点找该节点左子树的最大节点或者右子树的最小节点
Node* parent = cur;
Node* leftMax = cur->_left;
while (leftMax->_right)
{
parent = leftMax;
leftMax = leftMax->_right;
}
swap(leftMax->_key, cur->_key);//与替换节点交换值
//考虑特殊情况 找到的替换节点不是最后一个节点,它还有左孩子的情况
if (parent->_left == leftMax)
{
parent->_left = leftMax->_left;
}
else
{
parent->_right = leftMax->_left;
}
cur = leftMax;
}
delete cur;
return true;
}
}
//未找到
return false;
}
//递归实现
bool Erase(const K& key)
{
return _Erase(key, _root);
}
private:
bool _Erase(const K& key, Node*& root)
{
if (root == nullptr)
{
return false;
}
if (root->_key > key)
{
return _Erase(key, root->_left);
}
else if (root->_key < key)
{
return _Erase(key, root->_right);
}
else
{
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
Node* leftMax = root->_left;
while (leftMax->_right)
{
leftMax = leftMax->_right;
}
swap(leftMax->_key, root->_key);
//_Erase(key, root->_left);//最大节点可能还有左子孩子,所以还要继续递归删除---return
return _Erase(key, root->_left);
}
delete del;
return true;
}
}
搜索二叉树的遍历
搜索二叉树的中序遍历结果是有序的
//递归遍历
void _InOrder(Node* root)
{
if (root == NULL)//NULL和nullptr区别是什么
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
性能分析
对于二叉搜索树,如果是完全二叉树或者接近完全数,对于查找一个值的效率可以达到log2N,但是如果这棵树退化成单枝树或者或者接近单枝数,那么它的查找效率就变成了
O(N).