概念
二叉搜索树的概念
二叉树搜索树也是二叉树的一种,只是因为二叉树具有如下概念,因此可以实现一些特殊的功能
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
基于上面的特性,二叉搜索树是不允许进行修改的,因为一旦修改某个节点的值,那么这颗树就不符合二叉搜索树性质了。
因此我们从二叉搜索树的增删查三个方面来对其进行探讨。
实现
二叉搜索树插入元素
插入元素分成两步
- 树为空,直接创建根节点返回true
- 树不为空,插入元素与当前根节点大小进行比较,大于根节点向右走,小于根节点向左走,直到走到nullptr位置,在该位置新建节点插入。
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;
}
二叉搜索树查找元素
查找元素与插入类似
- 空树直接返回false
- 不为空时,查找元素与根节点,比较,相等返回true,查找元素大于当前节点的值就向右走,小于向左走。若走到nullptr未找到,则说明没有该元素,返回false。
bool Find(const K& key) {
while(_root) {
if (_root->_key > key) {
_root = _root->_left;
}
else if (_root->_key < key){
_root = _root->_right;
}
else {
return true;
}
}
return false;
}
二叉搜索树删除元素
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
- 要删除的结点无孩子结点
- 要删除的结点只有左孩子结点
- 要删除的结点只有右孩子结点
- 要删除的结点有左、右孩子结点
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
- 删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
- 删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
- 在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题
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 = _root->_right;
}
else if (parent->_key > cur->_key) {
parent->_left = cur->_right;
}
else {
parent->_right = cur->_right;
}
delete cur;
cur = nullptr;
}
else if (cur->_right == nullptr) {//右子树为空
if (cur == _root) {
_root = _root->_left;
delete cur;
}
else if (parent->_key > cur->_key) {
parent->_left = cur->_left;
delete cur;
}
else {
parent->_right = cur->_left;
delete cur;
}
}
else {
Node* rightMinPartent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left) {//找右子树最小的节点, 最左
rightMinPartent = rightMin;
rightMin = rightMin->_left;
}
cur->_key = rightMin->_key;//替换删除
//删除右子树最左节点
if (rightMin == rightMinPartent->_left) {
rightMinPartent->_left = rightMin->_right;
}
else {
rightMinPartent->_right = rightMin->_right;
delete rightMin;
}
}
return true;
}
}
return false; //没找到跳出
}
性能分析
二叉搜索树不管是插入元素还是删除元素,都需要先进行比较,因此比较小徐就代表了二叉搜索树的效率。
因此当二叉搜索树最优的时候,就是一棵完全二叉树O(log2N)
最坏的情况是一个单支树,形如链表。O(N)